VIM/Python 插入模式补全菜单

1 投票
1 回答
806 浏览
提问于 2025-04-18 02:38

我想创建一个VIM的插入模式补全弹出菜单,最好是直接通过Python的VIM模块来实现,如果必要的话也可以调用VIM命令。根据我的需求,如果这可能实现,我需要了解哪些内容呢?

需求

  • 根据输入修改菜单。可以像easymotion插件那样重新上色和替换选中的字符,或者完全替换内容,这可能会导致菜单大小的变化。
  • 可自定义的导航绑定,比如:
    • 用j/k进行垂直导航(部分重绘
    • 用h/l进行类别导航/细化(完全重绘
    • 其他作为类别/关系快捷键(完全
    • 其他作为源循环(完全
    • easymotion风格的绑定(部分
    • 其他相关功能的绑定(
    • 可选的,替代上述绑定的模糊过滤模式(捕获所有输入)(完全
  • 当菜单被销毁时运行某个动作的钩子
  • 如果选择了菜单项,能够替换之前的单词(在普通模式下很容易做到……但是,有没有比调用:norm更好的方法?)

我再次需要确认这些是否可能实现。如果可以,请提供关于所需功能、API或函数的具体文档。好吧,足够让我入门就行。

如果重要的话,我还不确定接口的具体细节,但菜单本身可能需要相当大,以容纳内容。

编辑:

我还没有尝试任何东西;我只是想知道我应该阅读或了解什么,以实现我需要的VIM特定功能。我现在只有一段Python代码,它接受一个句子和句子中的一个单词,确定词性,进行词形还原,并提供同义词。

现在应该很清楚,这实际上是一个提供类词典功能的VIM插件,增强了VIM内置的CTRL_X-CTRL_T功能。不幸的是,同义词是一个复杂的层次结构,例如可以查看thesaurus.comwordnet。下面是同义词选择过程的树形结构。为了提供有用的词典功能,我需要在插入模式补全弹出菜单中导航这棵树。通过自动推断词性,可以跳过第一步,当然,最初合并并显示所有意义的同义词是有意义的,而不考虑关系。然而,要确定词性,我需要访问整个句子,并且我仍然需要能够导航到意义选择菜单。我还希望能够提供一个临时缓冲区,详细说明当前高亮的弹出菜单项的单词定义和示例句子。因此,需要一个钩子;在菜单被销毁时销毁缓冲区。我描述的所有其他快捷键都是为了方便,比如按关系过滤、打开信息临时缓冲区或切换词性。

POS  Sense      Relationship      Synonym
N -> Sense 1 -> Relationship 1 -> Synonym 1
                                  Synonym 2
                                  ...
                                  Synonym n
             -> Relationship 2 -> Synonym 1
                                  ...
                                  Synonym n
             -> Relationship 3 -> Synonym 1
                                  ...
                                  Synonym n
     Sense 2 -> Relationship 1 -> ...
     Sense 3 -> Relationship 2 -> ...
V -> Sense 1 -> ...
A -> ...

插入补全弹出菜单非常棒,因为它们保持上下文:周围的文本不会改变,保持(大部分)可见,窗口不会被调整大小。我可以在一个单独的缓冲区中提供我需要的功能(比如一个unite插件),但我更不想这样。

在阅读:h complete-functions后:

如果refresh="always",那么每次更改前导文本时,complete-functions都会被重新调用。其目的是细化匹配,但这对同义词匹配意义不大,因为单词已经完整,而只是会被替换。除此之外,插入补全弹出菜单只提供最少的键映射:<CTRL-H>CTRL-L><CTRL-Y><CTRL-E>等。可以使用pumvisible()添加额外的映射,但这些映射会影响所有弹出菜单,因此不适用。当然,我可以使用<expr>来检查一个全局变量,比如g:popup-menu-is-thesaurus,但这种方法基本上是一堆黑科技。例如,要在菜单中显示一个新类别,我可以这样做:

:call Thesaurus#BindKey("h","w:Thesaurus#CategoryUp")
:call Thesaurus#BindKey("i","w:Thesaurus#InsertMode")

与:

function! Thesaurus#BindKey(key, function)
  inoremap <expr> a:key a:function
endfunction

function! Thesaurus#CategoryUp()
  if !b:popup-menu-is-thesaurus
    return v:char
  let b:thesaurus-category-index -= 1
endfunction

function! Thesaurus#InsertMode()
  if !b:popup-menu-is-thesaurus
    return v:char
  let b:thesaurus-mode = "insert"
endfunction

function! Thesaurus#CompleteFunc(findstart, base)
  if a:findstart
    ...
  else
    if b:thesaurus-mode = "insert"
      return Thesaurus#FuzzyMatch(base)
    else
      return Thesaurus#Redraw()
    endif
  endif
endfunction

function! Thesaurus#Redraw()
  ...
endfunction

* 显然我对VimL不太熟悉。另外,我使用v:char的方式正确吗?

当然,我不知道一旦选择了一个条目如何替换之前的单词,因为启用模糊匹配的唯一方法是让a:base为空。

编辑2:我该如何触发我的补全函数,最好是通过标准的词典风格补全快捷键i_CTRL-X_CTRL-T?我不想失去我的自定义补全(全局和用户定义的补全),而且由于弹出菜单在当前缓冲区中显示,与FuzzyFinder不同,我无法覆盖omnifunc。

也许理想的做法是完全控制菜单绘制功能,从头构建弹出菜单,并使用像tinymode、tinykeymap或submode这样的插件伪造一个新的插入补全弹出菜单模式。

1 个回答

2

请记住,插入模式下的补全菜单是用来完成匹配的,比如当你输入foo并触发补全时,它会提供foobarfoomatic这样的选项。并没有一个通用的插入模式菜单可以执行任意操作,实际上实现这样的功能可能不是个好主意。不过,有些插件(比如FuzzyFinder)错误地使用插入模式的补全菜单作为动态选择器(在一个临时缓冲区中,所以完成的内容会被丢弃)。

你可以在:help complete-function找到完整的说明和示例。如果这些内容不能满足你的需求或者还有疑问,那就说明你可能在尝试用它做一些它本来不适合的事情。

根据输入修改菜单。

每次你输入一个键,你的'completefunc'都会被重新调用,它可以提供一组(减少后的)匹配项。

可自定义的导航绑定

这不是个好主意。用户应该为所有的补全自定义这个功能(使用:imap <expr>pumvisible()条件)。

在菜单被销毁时运行的钩子

在最近的Vim版本中添加了CompleteDone事件,但通常唯一的操作应该是“插入匹配项”,这会自动完成。

如果选择了菜单项,能够替换之前的单词

在第一次调用你的'completefunc'时,你的函数可以指定基础列,也就是补全匹配项开始的位置。

撰写回答