1

I wrote a function for selecting a text region and replacing in it certain predefined characters:

fun! Tstrepl() range
    s/\%Va/\="\u2227"/ge  " 'a' to '∧'
    s/\%Vb/\="\u2228"/ge  " 'b' to '∨'
endfun

vnoremap QQ :call Tstrepl()<CR>

The problem is that the function doesn't behave as expected and I cannot figure out why.

The following text is handled OK:

BEFORE: bbbaaa
AFTER: ∨∨∨∧∧∧

But another text isn't handled OK:

BEFORE: aaabbb
AFTER: ∧∧∧bbb

What's wrong with the function?

  • 1
    I think this is because you use multi-byte characters in the replacement field of the substitution commands. And because Vim – probably – keeps track of the start/end of the visual selection via byte indexes (not character indexes). When you replace a character whose weight is 1 byte, with a character whose weight is 2 bytes, Vim should update the end of the visual selection it had saved originally; it probably doesn't. Does this code help? (make sure * is not in the value of 'cpoptions') – user938271 May 5 at 14:54
  • @user938271 Thanks for the solution! It works. – Al Berger May 5 at 15:09
  • @user938271 this should be a full answer, not a comment and paste-binned code. Please move your comment into an answer (and code with it) – D. Ben Knoble May 5 at 17:26
2

I think this is because you use multi-byte characters in the replacement field of the substitution commands. And because Vim – probably – keeps track of the start/end of the visual selection via byte indexes (not character indexes). When you replace a character whose weight is 1 byte, with a character whose weight is 2 bytes, Vim should update the end of the visual selection it had saved originally; it probably doesn't.

Try this code:

fu! s:Tstrepl() abort
    let vcol = [virtcol("'<"), virtcol("'>")]
    let pat = '\%>' . (vcol[0]-1) . 'v\%<' . (vcol[1]+1) . 'v'
    exe '*s/' . pat . 'a/\="\u2227"/ge'
    exe '*s/' . pat . 'b/\="\u2228"/ge'
endfu
xno <silent> QQ :<c-u>call <sid>Tstrepl()<cr>

Make sure * is not in the value of 'cpoptions'.

For more information, see:

:h /\%v
:h virtcol()
  • What is the range * in *s/ ? – D. Ben Knoble May 6 at 15:08
  • 1
    It's an alias for '<,'>. See :h cpo-star. – user938271 May 6 at 15:52
1

user938271 already gave an explanation why OP had this problem.
Here is another solution:

vnoremap QQ :s/\v%V[ab]/\=submatch(0)=='a'?"\u2227":"\u2228"/g<cr>
  • \v%V[ab] search a or b in visual area.
  • \=submatch(0)=='a'?"\u2227":"\u2228" replace a with "\u2227", b with "\u2228". submatch(0) is the whole matched text.

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.