9

我一直在为 vim 词库寻找一个好的解决方案。显然,该功能是内置的,但似乎每个人都使用的文件是 mthesaur.txt。虽然它在插入模式下的命令显示一个列表的意义上“有效”,但在我看来,结果在编程上是正确的,但不是超级有用。vim 在线词库插件工作得非常好,但是网络上的延迟以及对返回的缓冲区使用拆分的必要性并不理想。有人对此有意见吗?

4

3 回答 3

5

我编写了一个插件,可以解决您在此处提出的两个问题。

Vim 的多语言词库查询插件

它从两个方面改善了使用体验:更明智的同义词选择机制;以及更好、更灵活的同义词来源。

Thesaurus_query.vim 截屏

默认情况下,插件使用 vim 的消息框进行候选显示,每个同义词都用一个数字标记。它让用户通过输入数字来选择合适的替换光标下的单词。它的工作方式类似于 vim 的默认拼写更正提示。并大大减少了从一长串候选词中选择正确同义词的操作时间。

为了提高同义词候选的质量,使用了多个查询后端。对于英语用户,有两个值得注意。

  • thesaurus_com使用 Thesaurus.com 作为同义词源的后端
  • mthesaur_txt后端使用 mthesaur.txt 作为同义词源

thesaurus_com后端将立即工作。要使本地查询后端正常工作,您需要下载并通过设置 variable或指定 variablemthesaur.txt告诉插件它所在的位置。否则只有在线后端将起作用。thesaurusg:tq_mthesaur_file

默认情况下,将首先使用在线查询后端。但如果互联网不可用或太慢,当前 vim 会话中的未来查询将首先由本地查询后端处理,以减少延迟时间。这两个后端的优先级也可以手动更改(参见文档)。

为了解决延迟问题(通常在找不到单词时会突出),我引入了超时机制。你可以设置

let g:tq_online_backends_timeout = 0.6

如果您的互联网速度相当快。这样延迟可以减少到 0.6 秒以下。

不过,该插件是用 Python 编写的。因此,您可能希望将它与使用 Python 和/或 Python3 支持编译的 Vim 一起使用。

于 2016-02-28T08:47:56.673 回答
3

如果您的系统是类 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 的强大插件(见上文)相比,这个解决方案非常粗糙,但它很轻量级并且对我来说足够好用。我将它与四种不同语言的叙词表一起使用。

于 2017-01-20T01:04:22.400 回答
2

~/.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  
于 2017-10-09T11:32:58.683 回答