1

Say the line is:

^<space1><space2><tab1><tab2><space3><space4><tab3><tab4><space5><space6><tab5><tab6><space7><space8>blablabla

So how to match all these <space>?

(FYI, I use call matchadd("yqi_faulty_indent", '<THE_REGEX>') to highlight it)

  • With ^\t*\zs\ \+, it matches only <space1><space2>
  • With ^\s*\zs\ \+, it matches only <space7><space8> due to the greedy mode I think
  • With ^\s\{-}\zs\ \+, it matches only <space1><space2> due to the non-greedy mode I think
  • With \v((^\s*)@<= +), it matches what I want, BUT, with lookaround regex, there's unacceptable high CPU when there are long lines like seq 1 30000 | tr '\n' ' ' | fold -w 3000 | vim -

UPDATE

According to the important hint from Vivian's answer, below is my latest solution:

  • call matchadd("yqi_faulty_indent", '\v((^\s*)@80<= +)') highlights blankspaces in TAB indent
  • call matchadd("yqi_faulty_indent", '\v((^\s*)@80<=\t+)') highlights TABs in blankspace indent

While as mentioned in comment, there's size limitation, looking forwards furthur better solution...

2 Answers 2

1

I would do:

/\v((^\s*\t)@<= +|(^\s*)@<= +\t@=)/

For performance reason you could do (limiting the lookahead to 80 bytes):

/\v((^\s*\t)@80<= +|(^\s*)@80<= +\t@=)/
9
  • This also matches blanks in the middle of a line which isn't expected, not only the indent part Commented Nov 17, 2023 at 4:31
  • You are right :-) I have improved the pattern accordingly. Commented Nov 17, 2023 at 4:35
  • Actually, I use lookaround regex before, however it causes high CPU, please see my update, so I have to switch to \zs. Looking forward better solution Commented Nov 17, 2023 at 6:48
  • Thanks for the feedback :-) Commented Nov 17, 2023 at 7:39
  • 1
    @VivianDeSmedt if you're editing a file with 10M lines in it, you'll certainly notice how badly look-behind performs. Commented Nov 20, 2023 at 3:39
0

I would just search for ^\t*\zs \+\ze\t

This avoids a performance-killing look-behind: match any number of leading tabs, then at least one space, then at least one more tab.

The run of space characters are wrapped with start-of-replacement \zs and end-of-replacement \ze, so that you can use this inside a s/// to remove or replace just the space characters.

If you're wanting a pattern to use in syntax highlighting, you'd want to anchor at both start-of-line and end-of-previous-match: \%(^\|\zG\)\t*\zs \+\ze\t. Unfortunately \zG is not be available in all versions of Vim. (It's the equivalent of Perl's \G.)

If you don't have that then perhaps it would be acceptable simply to highlight all whitespace between the first and last misused spaces:

syntax match BadSpace "^\t*\zs \%(\s* \)\=\ze\t"
highlight BadSpace term=inverse gui=inverse

However if replacement is your intention, I suggest using the :retab command instead.

If you really want to search for any blanks rather than just ASCII space, then it gets slightly more complicated: ^\t*[^\t]\@=\s\+\t

The key is a single-character that simultaneously matches not-a-tab and matches blank.

2
  • Thanks a lot for your answer, but as I tried ^\t*\zs \+\ze\t only highlights the first occurence Commented Nov 22, 2023 at 3:11
  • I have to admit, I was assuming this would be used in some kind of search-and-replace, and so multiple occurrences in one line would be handled in turn. Commented Nov 22, 2023 at 3:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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