1

Given a line of text like the following

let FindTargetsByRegexp = { re -> {lnums -> s:_FindTargetsByRegexp(re, lnums) }}

I want the expression

\(\n\|[^A-Za-z]\)\zs\S

to stop on the same word boundaries that you would stop on with w or b. Vim features \< but it has a different definition (alphabetical only).

One such problem case occurs on {lnum in the above example, it will stop on { but not l. I'm trying to say with my expression that as long as the character before wasn't alphabetical, and the character on wasn't a space, it should be a match. It's the reverse case for (r, it stops on r, but not (.

I think my understanding is broken somewhere. But basically why doesn't \A\zs\S do the trick? { is non-alphabetical, l not a whitespace.

Ah, it's to do with what's already been consumed by the parser. \A\@<=\S (a look behind) works to solve the {l case, but my idea can't cover the sometext(r scenario (due to alphabetical before (), and of course -> behaving like a full word is even trickier. It might be better not to do this particular map with a regexp, curious how vim defines it with w. I need some way to group like characters together and say any character not like it.

\(\A\@<=\S\|\a\@<=[^ A-z]\) gets really close... only case is doesn't do well is ->, maybe possible but hard to think through.

1 Answer 1

1

First, we must understand how exactly vim implements the w command.

To start with, vim assigns every character a "class," where numerically: blank = 0, punctuation = 1, word = 2, emoji = 3, etc. For this answer, we will ignore unicode and emoji. In ASCII, blanks are space, tab, and null. Word characters are those in iskeyword or \k. Punctuation is most of everything else. The rules are more complex with unicode.

When pressing w, vim follows these rules

  1. Always move one character at least.
  2. If the cursor's class was > 0, then we continue to scan forward until we meet a character of a different class. If crossing a line, stop. If the line is empty, the cursor will end here.
  3. Finally, if the cursor is now on a blank, go to the next non-blank, whatever class that may be.

With this we can create an approximate regular expression. The goal is to find situations where the class changed, either from blank to word, or non-punctuation to punctuation.

/\<\|\(^$\)\|\([^[:keyword:][:space:]]\@<![^[:keyword:][:space:]]\)

Breaking this down,

/\<               handles all the word boundaries
     \(^$\)       handles empty lines
             \( [keyword, space, other]   [not keyword, or space]\)

We use [^[:keyword:][:space:]] to mean essentially punctuation. [:keyword:] is the same as \k but the latter cannot go inside a character class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.