VIM/Python 插入模式补全菜单
我想创建一个VIM的插入模式补全弹出菜单,最好是直接通过Python的VIM模块来实现,如果必要的话也可以调用VIM命令。根据我的需求,如果这可能实现,我需要了解哪些内容呢?
需求
- 根据输入修改菜单。可以像easymotion插件那样重新上色和替换选中的字符,或者完全替换内容,这可能会导致菜单大小的变化。
- 可自定义的导航绑定,比如:
- 用j/k进行垂直导航(部分重绘)
- 用h/l进行类别导航/细化(完全重绘)
- 其他作为类别/关系快捷键(完全)
- 其他作为源循环(完全)
- easymotion风格的绑定(部分)
- 其他相关功能的绑定(无)
- 可选的,替代上述绑定的模糊过滤模式(捕获所有输入)(完全)
- 当菜单被销毁时运行某个动作的钩子
- 如果选择了菜单项,能够替换之前的单词(在普通模式下很容易做到……但是,有没有比调用:norm更好的方法?)
我再次需要确认这些是否可能实现。如果可以,请提供关于所需功能、API或函数的具体文档。好吧,足够让我入门就行。
如果重要的话,我还不确定接口的具体细节,但菜单本身可能需要相当大,以容纳内容。
编辑:
我还没有尝试任何东西;我只是想知道我应该阅读或了解什么,以实现我需要的VIM特定功能。我现在只有一段Python代码,它接受一个句子和句子中的一个单词,确定词性,进行词形还原,并提供同义词。
现在应该很清楚,这实际上是一个提供类词典功能的VIM插件,增强了VIM内置的CTRL_X-CTRL_T
功能。不幸的是,同义词是一个复杂的层次结构,例如可以查看thesaurus.com或wordnet。下面是同义词选择过程的树形结构。为了提供有用的词典功能,我需要在插入模式补全弹出菜单中导航这棵树。通过自动推断词性,可以跳过第一步,当然,最初合并并显示所有意义的同义词是有意义的,而不考虑关系。然而,要确定词性,我需要访问整个句子,并且我仍然需要能够导航到意义选择菜单。我还希望能够提供一个临时缓冲区,详细说明当前高亮的弹出菜单项的单词定义和示例句子。因此,需要一个钩子;在菜单被销毁时销毁缓冲区。我描述的所有其他快捷键都是为了方便,比如按关系过滤、打开信息临时缓冲区或切换词性。
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 个回答
请记住,插入模式下的补全菜单是用来完成匹配的,比如当你输入foo
并触发补全时,它会提供foobar
和foomatic
这样的选项。并没有一个通用的插入模式菜单可以执行任意操作,实际上实现这样的功能可能不是个好主意。不过,有些插件(比如FuzzyFinder)错误地使用插入模式的补全菜单作为动态选择器(在一个临时缓冲区中,所以完成的内容会被丢弃)。
你可以在:help complete-function
找到完整的说明和示例。如果这些内容不能满足你的需求或者还有疑问,那就说明你可能在尝试用它做一些它本来不适合的事情。
根据输入修改菜单。
每次你输入一个键,你的'completefunc'
都会被重新调用,它可以提供一组(减少后的)匹配项。
可自定义的导航绑定
这不是个好主意。用户应该为所有的补全自定义这个功能(使用:imap <expr>
和pumvisible()
条件)。
在菜单被销毁时运行的钩子
在最近的Vim版本中添加了CompleteDone
事件,但通常唯一的操作应该是“插入匹配项”,这会自动完成。
如果选择了菜单项,能够替换之前的单词
在第一次调用你的'completefunc'
时,你的函数可以指定基础列,也就是补全匹配项开始的位置。