Vim - 在自动命令上定义缓冲区变量

wei*_*ith 2 vim


我想做以下事情:

autocmd BufAdd * let b:foo = 1
Run Code Online (Sandbox Code Playgroud)

但是正如文档所描述的那样,此时缓冲区仍然是旧缓冲区,并且只能由其访问<abuf>,这是新的缓冲区编号.

注意:执行此自动命令时,当前缓冲区"%"可能与正在创建的缓冲区"<afile>"不同.

测试显示,对于我的情况,它永远不是新缓冲区,并且始终为前一个缓冲区定义变量.所以我试着找出如何使用<afile>或者<abuf>将变量设置到缓冲区范围.


编辑:
从上面的最小例子可能问题不是很明显.设置此变量还有一个附加条件.因此,不会输入的每个缓冲区都会获得此变量(因此我能够区分它们).


编辑2: 因为@DoktorOSwaldo需要更多上下文.
我经常使用预览窗口来显示标签等.不幸的是,所有显示的缓冲区都被添加到缓冲区列表中并浪费内存使用(理论上).因此,简单的想法是定义一个自动命令,它会影响预览窗口中新打开的缓冲区.它必须是新的,因为用户自己使用的预期缓冲区是预期的.setlocal nobuflisted如果预览窗口中的缓冲区切换,这些缓冲区使本地选项不是listen()并且应该被清除.
对于工作流程案例,尚未打开的缓冲区在预览窗口中打开,导致用户现在想要编辑此缓冲区并在其他窗口中打开它.如果发生这种情况,我必须将缓冲区设置为再次列出,如果不再可见则不要擦除.因为简单地检查是否列出了缓冲区是不够的,因此这也将由许多特殊缓冲区(如文件浏览器或标记查看器)触发......因此,简单的解决方案应该是设置一个本地变量缓冲区加载到预览窗口中.之后,如果此缓冲区加载到此窗口之外,我可以通过此局部变量识别它.
关于某些代码行的想法:

augroup PreviewWindow
   autocmd!
   autocmd BufAdd * if &previewwindow | setlocal nobuflisted |
           \ setlocal bufhidden = 'wipe' | let b:preview = v:true | endif

   autocmd BufEnter * if ! &previewwindow && exists('b:preview') &&
           \ b:preview | setlocal buflisted | setlocal bufhidden = '' |
           \ let b:preview = v:false | endif
augroup END
Run Code Online (Sandbox Code Playgroud)



最终解决方案:
这是我的最终实现(现在似乎可行),使用标志使用两个事件的串联.

augroup PreviewWindow
  autocmd!

  " Global flag for event composition, if a new buffer has been added into the preview window.
  let g:new_preview = 0

  " -> a buffer is shown in the preview window, that does not exits so far
  " In this case the the buffer is not already loaded and setting local variables does not work.
  " Set a global flag that mark this event to handle it later on when the buffer is loaded.
  autocmd BufAdd * nested if &previewwindow |
        \ let g:new_preview = v:true |
        \ endif

  " -> event that should follow after a (new) buffer has entered to the preview window
  " Check if the global flag has been set, that this is a new preview buffer.
  " The buffer is now available and local settings and variables can be defined.
  " Make sure the buffer gets cleared after it loose its window.
  " Set a buffer variable to mark it for further events.
  autocmd BufWinEnter * nested if &previewwindow && g:new_preview |
    \ let g:new_preview = v:false |
    \ setlocal nobuflisted |
    \ setlocal bufhidden=wipe |
    \ let b:previewonly = v:true |
    \ endif

  " -> the buffer in the preview window will be opened anywhere else
  " In case a buffer is entered, that has the buffer variable to be a 'previewony ' buffer,
  " it will be remove from there, added to the buffer list again and will not
  " be deleted when not displayed anymore.
  autocmd BufEnter * nested 
     \ try |
     \   if b:previewonly |
     \     unlet b:previewonly |
     \     setlocal buflisted |
     \     setlocal bufhidden= |
     \   endif |
     \ catch |
     \ endtry

augroup END
Run Code Online (Sandbox Code Playgroud)


更多背景
原因有些人对我为什么要实现这种行为感到困惑,如编辑2中所述,我想解释它更详尽.也许事实证明我错了或者它只是一个特殊情况.让我们开始吧:
假设您正在处理三个文件
文件A:

include <File B> as B
include <File C> as C

function main() {
  call B.foo()
  call B.bar()
}
Run Code Online (Sandbox Code Playgroud)

档案B:

function foo() {
  return "foo"
}
Run Code Online (Sandbox Code Playgroud)

文件C:

function bar() {
  return "bar"
}
Run Code Online (Sandbox Code Playgroud)

因此,我们假设还有一些工具可以为项目中的每个函数生成一个标记(例如gutentags).
没有想象下面的窗口结构,只是简单地讨论它.
+ ------------------------------------------------- -------------- +
| |
| 预览|
+ ------------------------------ + ------------------ ------------- +
| | |
| 文件A | 文件B |
| | |
+ ------------------------------ + ------------------ ------------- +

1.案例
假设使用文件A在左侧窗口中工作.将光标移动到foo函数调用上,然后在预览窗口中打开光标处理下的单词.通过此文件B显示在预览窗口中,但仍然在右侧窗口中.对于这种情况,我不想在预览窗口中为缓冲区设置任何属性,因为它会影响我正在激活的缓冲区.因此,我的自动命令不会应用在预览窗口中打开的新缓冲区.

2.案例
继续并为该bar功能做同样的事情.因此,文件C在新缓冲区中打开,该缓冲区将显示在预览窗口中.现在我想在这个缓冲区上应用我的属性.导致文件C的缓冲区不应该在列表中,因此切换缓冲区有bnbp没有跳转到此缓冲区.此外,我希望在预览窗口中不显示此缓冲区时(实际上在预览窗口中打开不同的内容,就像在另一个文件中打开另一个标记一样).这是案例二.

3.案例
现在的最后一个案例,如果我用文件C打开新缓冲区来预览标签并在之后进行决定(当它在预览窗口中显示时)我将调整功能bar.所以我手动打开文件C(在文件A/... 的窗口中进行新的拆分).现在,以前隐藏的,应该在不再显示时被删除,得到一个我正在处理的"活动"缓冲区.所以我想决定何时保存这个缓冲区,或者关闭或隐藏或者......因此我要删除之前添加到它的所有这些属性.

另外我要提交我使用CursorHold事件尝试打开光标下的当前单词作为预览窗口中的标记.这是Vims文档/帮助本身的一个提议,并且是一个很好的节省时间,因为它使查找更快,并且只有在我停止工作时才生效(例如,考虑我目前使用的这个函数/对象).

希望这使我的观点更清楚.

Ing*_*kat 6

通常,您可以使用expand()将提到的特殊<afile>/ <abuf>转换为缓冲区名称/编号.该setbufvar()函数可以访问不同缓冲区中的变量.结合起来,这样的事情看起来像一种可能的方法:

autocmd BufAdd * call setbufvar(expand('<abuf>'), 'foo', 1)
Run Code Online (Sandbox Code Playgroud)

不幸的是,我得到了E94: No matching buffer for 16; 显然,BufAdd火灾时尚未为缓冲变量设置数据结构.

备择方案

至少,您获得缓冲区编号,并可以将其用作(全局)字典中的键来存储您的标志:

let g:foo = {} " Maps buffer numbers to boolean flags
autocmd BufAdd * let g:foo[expand('<abuf>')] = 1

" To check buffer "nr":
if get(g:foo, nr, 0) ...
Run Code Online (Sandbox Code Playgroud)