【问题标题】:Automatically resize vim gui according to number of vertical splits根据垂直分割的数量自动调整 vim gui 的大小
【发布时间】:2013-08-07 12:05:27
【问题描述】:

我正在为 gvim 编写一个小插件,它可以根据垂直分割的数量自动增加或减少 gui 的宽度。该插件的工作原理是这样的

if has("gui_running")
  augroup resize
    autocmd WinEnter * call <SID>ResizeSplits()
    autocmd WinLeave * call <SID>ResizeSplits()
    autocmd BufLeave * call <SID>ResizeSplits()
  augroup END
endif

这里ResizeSplits()是调整gui窗口大小的函数:

function! s:ResizeSplits()
  let l:count = 0
  windo   if winwidth(winnr()) < &columns | 
        \   let l:count += 1              |
        \ endif
  if l:count > 0
    let l:totwidth = l:count - 1 + l:count*80
  else
    let l:totwidth = 80
  endif
  if &columns != l:totwidth
    execute 'set co=' . l:totwidth
  endif
endfunction

该插件几乎可以按我的意愿工作,但并不完全。似乎BufLeave 事件(和类似事件)有时会在窗口关闭之前执行。例如,当我执行&lt;c-w&gt;o:only 时,这是一个问题。问题是ResizeSplits 函数不起作用,因为它仍然计算旧的窗口数。

是否有另一个自动命令可用于检测窗口数量何时发生变化,或者保证在窗口被销毁/删除后执行的类似BufLeave 的事件?

让我的插件与映射一起工作是微不足道的,但我无法让它与 :only:close 等 ex 命令可靠地工作。

【问题讨论】:

  • 一种解决方法是将&lt;c-w&gt;c 重新映射为&lt;c-w&gt;c:call ...&lt;cr&gt;(其他相关映射类似)。这确实对我有用,但感觉就像一个相当丑陋的黑客攻击。
  • 请提供更多背景信息;你的最终目标是什么?当一个问题只是一个很小的技术步骤时,很难提供一个好的答案。如果你不告诉我们为什么你想要这个,很容易屈服于XY problem
  • 啊,我发现我之前的假设是错误的:自动命令确实执行了,所以我真的屈服于 XY 问题。我已经用更具体的细节和更多的背景更新了这个问题。
  • 我没有 gvim 进行测试,但我不久前为某人写了类似的东西。我想它遇到了和你一样的问题(可能更多,因为我只使用 WinEnter)。不过,看到实现上的差异可能会很有趣。 stackoverflow.com/questions/7955473/…
  • 是的,情况是一样的(至少在原则上),我怀疑你的代码和我的代码有同样的问题。

标签: events vim buffer


【解决方案1】:

我找到了一个似乎效果很好的解决方案。首先,我重写了ResizeSplits函数:

function! s:ResizeSplits()
  let l:curwin = winnr()
  let l:colwidth = 80 + &foldcolumn
  if &number
    let l:colwidth += &numberwidth
  endif

  let l:count = 0
  windo   if winwidth(winnr()) < &columns |
        \   let l:count += getwinvar(winnr(), 'count') |
        \ endif
  if l:count > 0
    let l:totwidth = l:count - 1 + l:count*l:colwidth
  else
    let l:totwidth = l:colwidth
  endif

  if &columns != l:totwidth
    silent! execute 'set co=' . l:totwidth
    silent! execute 'wincmd ='
  endif
  silent! execute l:curwin . 'wincmd w'
endfunction

重要的变化是我定义了一个变量w:count,它可以是 0 或 1。该函数与以下自动命令一起使用:

if has("gui_running")
  augroup vimrc_autocommands
    autocmd WinEnter    * let w:count = 1 | call <SID>ResizeSplits()
    autocmd BufEnter    * let w:count = 1 | call <SID>ResizeSplits()
    autocmd WinLeave    * call <SID>ResizeSplits()
    autocmd BufHidden   * let w:count = 0 | call <SID>ResizeSplits()
    autocmd BufWinLeave * let w:count = 0 | call <SID>ResizeSplits()
  augroup END
endif

这似乎在我尝试过的几乎所有情况下都有效。只有一种情况:only&lt;c-w&gt;o 仍然不起作用:如果窗口具有相同的缓冲区。一个简单的映射补救&lt;c-w&gt;o

nnoremap <c-w>o <c-w>o:call <sid>ResizeSplits()<cr>

如果有人找到更好的解决方案,我当然会很高兴。

【讨论】:

    【解决方案2】:

    :close / :quit 没有确切的事件;最接近的是BufWinLeave,但是当缓冲区在另一个缓冲区中仍然可见时不会触发。您可以将其与BufLeave 结合使用,但随后必须检查缓冲区实际上是否不再可见。

    要仅处理未列出的缓冲区,您可以在执行的自动命令中添加条件检查'buflisted'

    问题更新后编辑

    我认为没有办法拦截您所描述的极端情况。特别是:only 可能会很棘手。我只能建议在CursorHoldCursorMoved 上使用额外的自动命令来解决这个问题。这样,不正确的状态只会持续很短的时间。

    【讨论】:

    • BufWinLeaveBufLeave 的自动命令不会针对未列出的缓冲区执行。
    • 对不起,前面的评论完全是错误的。问题已更新。
    猜你喜欢
    • 2011-05-21
    • 2021-07-05
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 2013-07-23
    • 2012-07-18
    • 2013-06-27
    • 1970-01-01
    相关资源
    最近更新 更多