This was not supposed to be my first post here. But I was writing that one and ran into a problem.
I write markdown in vim, and I hard wrap lines at 80 characters.
In my local preview everything was looking fine, but when pasting my post into the dev.to editor, I got greeted by a bunch of broken lines:
After some googling, I found out that there is an open issue with markdown linebreaks.
Handle linebreaks in markdown #575
Feature Request or Task
Feature request.
User Story / Description
I'm writing my articles in a code editor and line-break them manually at 80 chars, so I could read them anywhere without worrying about word-wrap and stuff. Markdown does not count these line-breaks as line-breaks and every markdown parser I saw join them automatically, so
Hello
world
becomes
Hello world
Dev.to, however, handle them as line-breaks.
Definition of Done
Hello
world
in a dev.to article renders as
Hello world
What to do? I could switch to soft linebreaks, but navigation gets cumbersome. Maybe I could try to use the dev.to API to post and preprocess the markdown before... sounds like more work than I'd like to do. What's a lazy good enough approach?
Have vim do it!
Can I edit the text with my usual setup but copy it as if it didn't have linebreaks? You can guess that was a yes. Here's the little monster I came up with
:map ,r :let oldtw=&textwidth <cr> \|:set textwidth=10000 <cr> \| ggVGgqgvy \| :let &textwidth=oldtw <cr> \| :set textwidth? <cr> \| gvgq
Let's break it down (omitting \|
which is a command separator and <cr>
which just means press enter after typing the command):
:map ,r
:let oldtw=&textwidth
:set textwidth=10000
ggvGgqgvy
:let &textwidth=oldtw
:set textwidth?
gvgq
:map ,r
on pressing the ,r shortcut...
:let oldtw=&textwidth
store whatever your current textwidth
is (in my case 80)
:set textwidth=10000
set textwidth
to some ridiculously large value so that you get no line breaks on long paragraphs
ggvGgqgvy
hah
-
gg
: go to the top of the file, -
v
: enable visual mode, -
G
: go to the bottom of the file (since you're in visual mode this will select everything) -
gq
: reformat the paragraph (sincetextwidth
is now 10000 all your paragraphs will be without newlines)
:let &textwidth=oldtw
Restore the textwidth
property to this old values (warning! this is not enough!, see options as values in vimscript)
:set textwidth?
Value is now fully restored
gvgq
-
gv
: start visual mode with the last selection (so whole document) -
gq
: reformat the paragraph again with your oldtextwidth
And that's it! Your post will be on your clipboard with uninterrupted paragraphs, and your editor will still display your nicely wrapped text.
Admittedly it has a few corner cases and yes, I should turn it into a function but solving problems with this uber pragmatic approach is a lot of fun. And fast. And I wanted to get something posted already!
Top comments (4)
This post really stuck with me lol, I read it on my way home and have been thinking about it the back of my head all evening.
I really wanted to replicate this using a regex search, cause I really thought I could lol and here's what I got!
So now it's my turn to break it down! So we have that commands here, first we do a regex substitution on all the lines, then we yank all the lines, and finally we undo the substitution. Lets look at each bit!
I use
%
twice here and that means to do the command on all the lines in the file! This is nifty for both the substitution and the yankingHere is the substitution on it's own
and even farther here is the regex we are using to search. This is slightly altertered from the real vim syntax above. I replace
\(
and\)
with just(
and)
. In vim substitutions you need to escape the parens you are using as capture groups. I replaced them down below cause I find it more readable like thisWe are actually actually going to ignore that first capture group right now and start with
This is where I started, and this matches a new line, and then captures the next character which must NOT be a new line. Now I was able to replace the new line with a space! I noticed however that this did NOT preserve blank lines, which I didn’t like (and didn’t match your implementation).
This is where that first capture group comes in!
([^\_^])
. This captures a single charactur and makes sure it is NOT a\_^
which is a VIM special character that means ‘beggining of line’. In practice what this does is make sure there is some character proceeding our newline. This allows blank lines to not be captured, and therefor not be altered by the substitution!And then so stitch that all together we then stitch our first and second capture group together seperated by a space (where there used to be a newline).
The rest of the command is much simpler!
is gonna yank the whole file for us, and then
is gonna undo our substitution!
What do you think? Any edge cases that I missed that yours catches?
(Oh and I'm excited for your second dev.to post now too! 😉)
Hah nice! And shorter! and with regex! This is super elegant.
Not sure about edge cases but I'll give it a go and let you know. On a first look, your undo gets rid of one of my annoyances, which is that my solution was reformatting code blocks too, which you might want to leave untouched.
It also doesn't rely on where you are on the file. Mine was breaking if I was on the bottom line.
So far yours def wins in my book. Making it more worth having shared my solution. Thanks!
Ahh I didn't think about code blocks really. Mine is gonna single line all codeblocks I think lol Which probably isn't the best!
So that's at least one edge case to think about!
Thanks again for the fun post, I enjoyed it haha
I love this so much! I love to seeing fun ways to use VIM.
Thanks for the cool post, and great explanation of how it all works!