3

I have a command mapping ("ESC-P") in my .vimrc which underlines the current line, leaving whitespace or # intact.

map ^[p :.t.^M:s/[^[:space:]#]/-/g^M

If I run this on a line of text, it nicely underlines it.

this is a test line ---- -- - ---- ---- 

Likewise, it works nicely on comments:

# this is a test comment # ---- -- - ---- ------- 

But it I have any other hashtags in the line, they're also left alone. I'd like to replace them all EXCEPT for one that occurs at the start of a line.

For instance, I get:

# # # ## # This is a messy comment # # # ## # ---- -- - ----- ------- this has a hashtag # here ---- --- - ------- # ---- 

But what I'd like to get is this:

# # # ## # This is a messy comment # - - -- - ---- -- - ----- ------- this has a hashtag # here ---- --- - ------- - ---- 

I've spent some time on this and can't come up with a clean way to match all whitespace plus a # only in the first column.

Any ideas?

(As an aside, anyone know why \s or [:s:] don't work properly inside of a character class in vim? Only [:space:] works here.)

2
  • 1
    Vim (and vi and ex and ed and sed) are not great at doing this kind of editing. I think your best approach is to split your copied line after the first character. On the second line do your "not-space to " conversion. On the first line do your "not (space or #) to" conversion, and then join the two lines together. Maybe use a register to hold the first character? I could write this up if this is not enough of a hint. Commented Jun 12, 2024 at 4:33
  • 1
    You might get a quicker response in vim forum: vi.stackexchange.com Commented Jun 12, 2024 at 6:48

1 Answer 1

1

Could be:

s/\v[^#[:space:]]|([^#[:space:]].*)@<=#/-/g 

Using the positive look-behind @<= operator (similar to perl's (?<=...) except perl's does not support that to match with variable length). That is replace a character other than # and whitespace or a # provided it's preceded by at least one character other than # and whitespace.

Bound to a key:

map ^[p yyp:s/\v[^#[:space:]]|([^#[:space:]].*)@<=#/-/g^M 

As to your side question, [\s] is required by POSIX to match on either backslash or s for vi, which is likely the reason why it also does in vim.

1
  • Lookarounds are new to me and I've been trying to suss out the second stanza here. It doesn't quite work the way I was hoping, but after digging, I realized that what I wanted was simpler. As written it leaves all of the # after the initial one in my 3rd example; by removing [:space:] in the lookbehind, it works there, but I found another case: Lines that start with more than one #. But you sent me in the right direction, and I came up with this simple solution: yyp:s/\v[^#[:space:]]|(.)@<=#/-/g I'm still unclear on how your lookbehind works. Can you explain in more detail? Commented Jun 14, 2024 at 17:58

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.