3

When I was editing a fairly large text, I accidentally hit gq* instead of gq} and then vim apparently seemed to freeze a moment with the message search hit BOTTOM, continuing at TOP displayed in the command line. I found out it was because the wrapscan option was enabled by default and when the word under cursor was not found between the cursor position and the end of the file, the search continued from the top.

But I should say this is not what we normally expect when formatting text. Besides, the gq command behaves rather weirdly when search wraps round. So I'm trying to figure out how to set the nowrapscan option when formatting text with gq. Though I barely know anything about scripting, I managed to write the following function after reading a couple of topics in help:

nnoremap <F4> :set opfunc=GqNoWrapscan<CR>g@

function! GqNoWrapscan(type)
    let s:wrapscan = &wrapscan
    set nowrapscan
    silent exe "normal! '[gq']"
    let &wrapscan = s:wrapscan
    unlet! s:wrapscan
endfunction

Unfortunately, this function does not work as expected. I still get the same message and the nowrapscan option is apparently not set when I hit <F4>*. Could anyone help?

1

g@ works like this: vim waits for a motion, does the motion, stuffs the beginning and end cursor positions into the [ and ] marks, then calls your opfunc. So, by the time GqNoWrapscan is entered, the * search has already been done, and wrapscan was on. You would need to set nowrapscan before the g@. Unfortunately, what you are trying to accomplish is non-trivial, since if you provide an invalid motion (or press escape), you will wind up with wrapscan changed with no way to change it back.

One alternative approach (just for illustration) would be to remap * in operator-pending mode so that if it comes after a gq, it does not use wrapscan.

function! StarNoWrap()
    let l:wrapscan = &wrapscan
    if v:operator ==# 'gq'
        set nowrapscan
    endif
    try
        exe 'normal! '.v:count.'*'
    catch /^Vim\%((\a\+)\)\=:E385/
        " search hit BOTTOM without match
        " ought to print an error message here
    endtry
    let &wrapscan = l:wrapscan
endfunction

onoremap * :<c-u>call StarNoWrap()<cr>

An even simpler and more practical approach would be to just do

function! GqNoWrap()
    let l:wrapscan = &wrapscan
    set nowrapscan
    normal! gq*
    let &wrapscan = l:wrapscan
endfunction

nnoremap gq* :<c-u>call GqNoWrapScan()<cr>

" only if you don't want to override regular gq:
nnoremap <F4> gq
nnoremap <F4>* :<c-u>call GqNoWrapScan()<cr>

You will also have to handle # and possibly n, N and a few other commands.

  • Thanks for your prompt answer, Mass. I see. It is how g@ works and actually I kind of suspected as much, because my original function successfully set nowrapscan when I commented out the last two lines, though then it naturally failed to restore the original setting. Your approaches worked just as I desired. As far as I understand, there is no simple approach that covers various motions (*, #, etc.) with a single function. If that is the case, then wouldn't it rather make sense if wrapcan was disabled in gq in the first place? I don't see why it isn't. – tosh Aug 27 '17 at 4:45
  • Consistency, I suppose. vim commands generally consist of an operator (like gq = reformat), followed by a motion (like * = cword search forward). It would be more complicated to implement, document, and use if motions behaved differently depending on the preceding operator. There are few examples of this in vim, most notably cw behaves like ce not like c (change) then w (start of next word), try it!. Besides the map approaches I mentioned, you could also set nowrapscan globally and, or create a simple mapping to toggle it nnoremap <leader>w :set invwrapscan<cr> – Mass Aug 29 '17 at 1:18
  • Thanks for further clarifying the matter. I'm kind of a newbie with vim, so motions and operators are some of the things I'm not yet quite familiar with. Since setting nowrapscan globally is not exactly what I want, I think it best to follow the approaches you suggested until I learn more about these things and perhaps some marvellous idea comes to me one of these days. – tosh Aug 29 '17 at 13:50

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.