loading...

Yank hard wrapped text, or how to paste your vim markdown into dev.to

jesusgollonet profile image jesús gollonet ・2 min read

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

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 (since textwidth 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 old textwidth

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!

Discussion

pic
Editor guide
Collapse
coreyja profile image
Corey Alexander

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!

:%s/\([^\_^]\)\n\([^\n]\)/\1 \2/ | %y | u

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 yanking

Here is the substitution on it's own

s/\([^\_^]\)\n\([^\n]\)/\1 \2/

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 this

([^\_^])\n([^\n])

We are actually actually going to ignore that first capture group right now and start with

\n([^\n])

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).

s/\([^\_^]\)\n\([^\n]\)/\1 \2/

The rest of the command is much simpler!

%y

is gonna yank the whole file for us, and then

u

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! 😉)

Collapse
jesusgollonet profile image
jesús gollonet Author

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!

Collapse
coreyja profile image
Corey Alexander

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

Collapse
coreyja profile image
Corey Alexander

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!