我一直在为 vim 词库寻找一个好的解决方案。显然,该功能是内置的,但似乎每个人都使用的文件是 mthesaur.txt。虽然它在插入模式下的命令显示一个列表的意义上“有效”,但在我看来,结果在编程上是正确的,但不是超级有用。vim 在线词库插件工作得非常好,但是网络上的延迟以及对返回的缓冲区使用拆分的必要性并不理想。有人对此有意见吗?
3 回答
我编写了一个插件,可以解决您在此处提出的两个问题。
它从两个方面改善了使用体验:更明智的同义词选择机制;以及更好、更灵活的同义词来源。
默认情况下,插件使用 vim 的消息框进行候选显示,每个同义词都用一个数字标记。它让用户通过输入数字来选择合适的替换光标下的单词。它的工作方式类似于 vim 的默认拼写更正提示。并大大减少了从一长串候选词中选择正确同义词的操作时间。
为了提高同义词候选的质量,使用了多个查询后端。对于英语用户,有两个值得注意。
thesaurus_com
使用 Thesaurus.com 作为同义词源的后端mthesaur_txt
后端使用 mthesaur.txt 作为同义词源
thesaurus_com
后端将立即工作。要使本地查询后端正常工作,您需要下载并通过设置 variable或指定 variablemthesaur.txt
告诉插件它所在的位置。否则只有在线后端将起作用。thesaurus
g:tq_mthesaur_file
默认情况下,将首先使用在线查询后端。但如果互联网不可用或太慢,当前 vim 会话中的未来查询将首先由本地查询后端处理,以减少延迟时间。这两个后端的优先级也可以手动更改(参见文档)。
为了解决延迟问题(通常在找不到单词时会突出),我引入了超时机制。你可以设置
let g:tq_online_backends_timeout = 0.6
如果您的互联网速度相当快。这样延迟可以减少到 0.6 秒以下。
不过,该插件是用 Python 编写的。因此,您可能希望将它与使用 Python 和/或 Python3 支持编译的 Vim 一起使用。
如果您的系统是类 unix 并且安装了 awk,那么我有一个简单的解决方案来解决您的问题,让您无需互联网连接也无需拆分窗口即可访问多种语言的叙词表。
首先从以下位置下载 LibreOffice 叙词表:
https://cgit.freedesktop.org/libreoffice/dictionaries/tree/
例如。
(注意 th_*.dat 文件,这些是您需要的,而不是仅适用于 Hunspell 拼写检查的 .aff 和 .dic 文件。)下载您喜欢的 *.dat 叙词表并将它们复制到放置插件的文件夹;这个子目录应该叫做“thes”。
现在在您的插件文件夹中创建一个新文件(您应该在其中包含带有 *.dat thesauri 的“thes”子目录的文件夹)并将以下内容放入此文件中:
" offer choice among installed thesauri
" ==================================================
let s:thesaurusPath = expand("<sfile>:p:h") . "/thes"
function! s:PickThesaurus()
" 1, 1: glob does not ignore any pattern, returns a list
let thesaurusList = glob(s:thesaurusPath . "/*", 1, 1)
if len(thesaurusList) == 0
echo "Nothing found in " . s:thesaurusPath
return
endif
let index = 0
let optionList = []
for name in thesaurusList
let index = index + 1
let shortName = fnamemodify(name, ":t:r")
let optionList += [index . ". " . shortName]
endfor
let choice = inputlist(["Select thesaurus:"] + optionList)
let indexFromZero = choice - 1
if (indexFromZero >= 0) && (indexFromZero < len(thesaurusList))
let b:thesaurus = thesaurusList[indexFromZero]
endif
endfunction
command! Thesaurus call s:PickThesaurus()
:Thesaurus
这将允许您通过在 Vim 的命令模式中键入来选择您选择的词库。
(实际上,如果您打算只使用一个词库,那么您不需要任何这些;只需将词库文件的全名分配给缓冲区局部变量,b:thesaurus
)。
最后,将以下内容添加到您的插件文件中:
" run awk on external thesaurus to find synonyms
" ==================================================
function! OmniComplete(findstart, base)
if ! exists("b:thesaurus")
return
endif
if a:findstart
" first, must find word
let line = getline('.')
let wordStart = col('.') - 1
" check backward, accepting only non-white space
while wordStart > 0 && line[wordStart - 1] =~ '\S'
let wordStart -= 1
endwhile
return wordStart
else
" a word with single quotes would produce a shell error
if match(a:base, "'") >= 0
return
endif
let searchPattern = '/^' . tolower(a:base) . '\|/'
" search pattern is single-quoted
let thesaurusMatch = system('awk'
\ . " '" . searchPattern . ' {printf "%s", NR ":" $0}' . "'"
\ . " '" . b:thesaurus . "'"
\)
if thesaurusMatch == ''
return
endif
" line info was returned by awk
let matchingLine = substitute(thesaurusMatch, ':.*$', '', '')
" entry count was in the thesaurus itself, right of |
let entryCount = substitute(thesaurusMatch, '^.*|', '', '')
let firstEntry = matchingLine + 1
let lastEntry = matchingLine + entryCount
let rawOutput = system('awk'
\ . " '" . ' NR == ' . firstEntry . ', NR == ' . lastEntry
\ . ' {printf "%s", $0}' . "'"
\ . " '" . b:thesaurus . "'"
\)
" remove dash tags if any
let rawOutput = substitute(rawOutput, '^-|', '', '')
let rawOutput = substitute(rawOutput, '-|', '|', 'g')
" remove grammatical tags if any
let rawOutput = substitute(rawOutput, '(.\{-})', '', 'g')
" clean spaces left by tag removal
let rawOutput = substitute(rawOutput, '^ *|', '', '')
let rawOutput = substitute(rawOutput, '| *|', '|', 'g')
let listing = split(rawOutput, '|')
return listing
endif
endfunction
" configure completion
" ==================================================
set omnifunc=OmniComplete
set completeopt=menuone
这将允许您获取您在插入模式下键入的任何单词的同义词。仍处于插入模式时,按 Ctrl-X Ctrl-O(或您在omnicompletion 上映射的任何组合键),弹出菜单将显示同义词列表。
与 Chong 的强大插件(见上文)相比,这个解决方案非常粗糙,但它很轻量级并且对我来说足够好用。我将它与四种不同语言的叙词表一起使用。
~/.vimrc 的脚本,它需要文件 thesaurii.txt(来自https://github.com/moshahmed/vim/blob/master/thesaurus/thesaurii.txt的合并字典)和 perl.exe 用于搜索同义词的路径. 在 win7 和 cygwin perl 上测试的脚本。
如果没有找到同义词,则调用 aspell 进行拼写更正。有关如何在按 [tab] 时调用此函数的信息,请参阅https://stackoverflow.com/a/53825144/476175 。
set thesaurus=thesaurii.txt
let s:thesaurus_pat = "thesaurii.txt"
set completeopt+=menuone
set omnifunc=MoshThesaurusOmniCompleter
function! MoshThesaurusOmniCompleter(findstart, base)
" == First call: find-space-backwards, see :help omnifunc
if a:findstart
let s:line = getline('.')
let s:wordStart = col('.') - 1
" Check backward, accepting only non-white space
while s:wordStart > 0 && s:line[s:wordStart - 1] =~ '\S'
let s:wordStart -= 1
endwhile
return s:wordStart
else
" == Second call: perl grep thesaurus for word_before_cursor, output: comma separated wordlist
" == Test: so % and altitude[press <C-x><C-o>]
let a:word_before_cursor = substitute(a:base,'\W','.','g')
let s:cmd='perl -ne ''chomp; '
\.'next if m/^[;#]/;'
\.'print qq/$_,/ if '
\.'/\b'.a:word_before_cursor.'\b/io; '' '
\.s:thesaurus_pat
" == To: Debug perl grep cmd, redir to file and echom till redir END.
" redir >> c:/tmp/vim.log
" echom s:cmd
let s:rawOutput = substitute(system(s:cmd), '\n\+$', '', '')
" echom s:rawOutput
let s:listing = split(s:rawOutput, ',')
" echom join(s:listing,',')
" redir END
if len(s:listing) > 0
return s:listing
endif
" Try spell correction with aspell: echo mispeltword | aspell -a
let s:cmd2 ='echo '.a:word_before_cursor
\.'|aspell -a'
\.'|perl -lne ''chomp; next unless s/^[&]\s.*?:\s*//; print '' '
let s:rawOutput2 = substitute(system(s:cmd2), '\n\+$', '', '')
let s:listing2 = split(s:rawOutput2, ',\s*')
if len(s:listing2) > 0
return s:listing2
endif
" Search dictionary without word delimiters.
let s:cmd3='perl -ne ''chomp; '
\.'next if m/^[;#]/;'
\.'print qq/$_,/ if '
\.'/'.a:word_before_cursor.'/io; '' '
\.&dictionary
let s:rawOutput3 = substitute(system(s:cmd3), '\n\+$', '', '')
let s:listing3 = split(s:rawOutput3, ',\s*')
if len(s:listing3) > 0
return s:listing3
endif
" Don't return empty list
return [a:word_before_cursor, '(no synonyms or spell correction)']
endif
endfunction