odeke-em / vim

Automatically exported from code.google.com/p/vim
0 stars 0 forks source link

NoMatchParen might cause E201: *ReadPre autocommands must not change current buffer #345

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
When using "NoMatchParen" (from the matchparen plugin in the runtime) via
a *ReadPre autocommand this might cause a E201, because it uses "windo"
 and then the "(curbuf != old_curbuf)" check in Vim's readfile function fails.

What steps will reproduce the problem?
1. vim -u NONE -N -p file1 file2
2. runtime plugin/matchparen.vim
3. au BufReadPre * NoMatchParen
4. !echo foo >> file2
5. Vim will ask to reload the file, pressing "l" will cause the error:

E201: *ReadPre autocommands must not change current buffer
E321: Could not reload "file2"

The following patch fixes it.  Given how necessary such an Windo function
usually is, which restores the previous window, I think it would be useful to
have it provided by Vim itself somehow.  The same applies to "bufdo".

    diff --git i/runtime/plugin/matchparen.vim w/runtime/plugin/matchparen.vim
    index 3804ab9..396ff32 100644
    --- i/runtime/plugin/matchparen.vim
    +++ w/runtime/plugin/matchparen.vim
    @@ -181,9 +181,16 @@ function! s:Highlight_Matching_Pair()
     endfunction

     " Define commands that will disable and enable the plugin.
    -command! NoMatchParen windo silent! call matchdelete(3) | unlet! g:loaded_matchparen |
    +function! s:Windo(command)
    +    let curaltwin = winnr('#') ? winnr('#') : 1
    +    let currwin = winnr()
    +    execute 'windo ' . a:command
    +    execute curaltwin . 'wincmd w'
    +    execute currwin . 'wincmd w'
    +endfunction
    +command! NoMatchParen call s:Windo('silent! call matchdelete(3)') | unlet! g:loaded_matchparen |
              \ au! matchparen
    -command! DoMatchParen runtime plugin/matchparen.vim | windo doau CursorMoved
    +command! DoMatchParen runtime plugin/matchparen.vim | call s:Windo('doau CursorMoved')

The following patch fixes it in LargeFile itself:

    diff --git i/plugin/LargeFile.vim w/plugin/LargeFile.vim
    index 41a90d3..8b21c0c 100644
    --- i/plugin/LargeFile.vim
    +++ w/plugin/LargeFile.vim
    @@ -73,7 +73,7 @@ fun! s:LargeFile(force,fname)
          au BufLeave       <buffer>        call s:LargeFileLeave()
            endif
            au WinEnter             *                       call s:LargeFileWinEnter()
    -    au BufUnload       <buffer>        augroup LargeFileAU|au! <buffer>|augroup END
    +    au BufUnload       <buffer>        augroup LargeFileAU|exec 'au! * <buffer>'|augroup END
        augroup END
        let b:LargeFile_mode = 1
     "   call Decho("turning  b:LargeFile_mode to ".b:LargeFile_mode)
    @@ -105,7 +105,14 @@ fun! s:ParenMatchOff()
        redir END
        if matchparen_enabled =~ 'g:loaded_matchparen'
            let b:LF_nmpkeep= 1
    +    " Disable matchparen. (Re)store current window numbers, it uses "windo",
    +    " and this might cause 'E201: *ReadPre autocommands must not change
    +    " current buffer'.
    +    let curaltwin = winnr('#') ? winnr('#') : 1
    +    let currwin = winnr()
            NoMatchParen
    +    execute curaltwin . 'wincmd w'
    +    execute currwin . 'wincmd w'
        endif
     "  call Dret("s:ParenMatchOff")
     endfun

Original issue reported on code.google.com by dhahler@gmail.com on 20 Mar 2015 at 6:52

GoogleCodeExporter commented 9 years ago
I fully agree that such :windo extension as provided by your s:Windo() function 
is necessary for most plugin uses. My ArgsAndMore plugin 
(http://www.vim.org/scripts/script.php?script_id=4152) provides such extended 
:Windo command (and :Bufdo, :Argdo, :Tabdo, etc.) also for interactive use.

Besides saving and restoring the original and previous windows, the window 
layout may also be affected by :windo; namely, windows with a height / width of 
0 (frequently used in "Rolodex mode") will increase to size 1 by entering. This 
can be undone by wrapping the :windo with this:

    let l:originalWindowLayout = winrestcmd()
    windo ...
    silent! execute l:originalWindowLayout

Full implementation:

    +function! s:Windo(command)
    +    let l:originalWindowLayout = winrestcmd()
    +    let curaltwin = winnr('#') ? winnr('#') : 1
    +    let currwin = winnr()
    +    execute 'windo ' . a:command
    +    execute curaltwin . 'wincmd w'
    +    execute currwin . 'wincmd w'
    +    silent! execute l:originalWindowLayout
    +endfunction

Original comment by sw...@ingo-karkat.de on 20 Mar 2015 at 7:12