Thesaurus is a very useful function for anybody who wants to write a longer text. Vim's support for this functionality is rudimentary at best and it has plenty of shortcomings. These two Reddit posts lists some of them (while discussing using Mobby Thesaurus as a source of data):
mthesaur.txt contains whole phrases as synonyms - eg 'under the weather' for 'down'. Vim then suggests 'under', 'the' and 'weather' as seperate matches.
Because of the way Vim finds auto-complete matches, the suggestions sometimes number upwards of 10,000 making it really unusable.
Vim can't accept lines longer than 512 characters. Since each entry in mthesaur.txt is one line, they can contain many thousands of characters.
There are some Vim plugins which are dealing with some of these problems (e.g., https://github.com/Ron89/thesaurus_query.vim, https://github.com/reedes/vim-lexical.git), but unfortunately the first thing they have to do is to completely ignore built-in thesaurus in vim (i_CTRL-X_CTRL-T), because this key combination is hardcoded.
Example of omnifunc, where lively ecosystem of completion engines flourishes, and the negative example of both thesaurus and spellchecking, which are limited to the limited built-in implementations (I just have to mention my old PR #2500 and yes, in the end, it seems to me that the original solution before the built-in spellchecker was added could be better if better integrated and/or some simple spelling engine was included).
Some of these issues were mentioned in #1611 but they were mostly ignored so far.
There are some heroic efforts to make the built-in thesaurus working, but none of them seem to be able to overcome the problems in the built-in implementaiton.
Describe the solution you'd like
Introduce configuration settings thesaurusfunc (and possibly even spellsfunc) with the similar syntax to omnifunc, which would allow using external engine for thesaurus functionality.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
Triage notifications on the go with GitHub Mobile for iOS or Android.
![]()
The support for the 'thesaurusfunc' option has been merged now 160e994 (patch 8.2.3520).
—
You are receiving this because you commented.
I have set up my thesaurusfunc to work with the well known aiksaurus CLI Thesaurus tool. I am sharing my config here, in case it is useful to someone else:
"vimrc func Thesaur(findstart, base) if a:findstart let line = getline('.') let start = col('.') - 1 while start > 0 && line[start - 1] =~ '\a' let start -= 1 endwhile return start else let res = [] let h = '' for l in split(system('aiksaurus '.shellescape(a:base)), '\n') if l[:3] == '=== ' let h = substitute(l[4:], ' =*$', '', '') elseif l[0] =~ '\a' call extend(res, map(split(l, ', '), {_, val -> {'word': val, 'menu': '('.h.')'}})) endif endfor return res endif endfunc if has('patch-8.2.3520') set thesaurusfunc=Thesaur endif
—
You are receiving this because you commented.
I have set up my
thesaurusfuncto work with the well knownaiksaurusCLI Thesaurus tool. I am sharing my config here, in case it is useful to someone else:
Seriously not bad, thank you. However, I have a bit of comments:
madhouse. Plain aiksaurus reply is:plugin@stitny (master)$ aiksaurus madhouse
*** No synonyms known. ***
Alphabetically similar known words are:
mad
mad about
mad dog
madam
madame
madcap
madden
maddening
made
mademoiselle
madly
madman
madness
madrigal
Mae West
maelstrom
maestro
Mafia
Mafioso
magazine
No synonyms known. error—
You are receiving this because you commented.
- your function doesn't have any protection against
No synonyms known.error- perhaps those similar words would be a good potential substitution? Or perhaps not.
The following updated snippet supports showing the alphabetical suggestions for the No synonyms known. error. It shows a crystal ball next to the suggestions, so that it is clear that no synonyms were found and that it is just guessing. You can easily replace the crystal ball 🔮 with another character, if your terminal does not support these Emoji. I have also updated the original snippet above.

func Thesaur(findstart, base) if a:findstart let line = getline('.') let start = col('.') - 1 while start > 0 && line[start - 1] =~ '\a' let start -= 1 endwhile return start else let res = [] let h = '' for l in split(system('aiksaurus '.shellescape(a:base)), '\n') if l[:3] == '=== '
let h = '('.substitute(l[4:], ' =*$', ')', '') elseif l ==# 'Alphabetically similar known words are: ' let h = '🔮' elseif l[0] =~ '\a' || (h ==# '🔮' && l[0] ==# "\t") call extend(res, map(split(substitute(l, '^\t', '', ''), ', '), {_, val -> {'word': val, 'menu': h}}))
endif endfor return res endif endfunc
set thesaurusfunc=Thesaur
—
You are receiving this because you commented.
Could you make it into a separate plugin, so we have issue tracker, pull requests, etc., please?
I am not sure if this little snippet is worth it to create an extra repo for it. But I do see that in f4d8b76 this snippet was added to the official vim help, so maybe Bram could update it there.
The diff would be (it also includes a small optimization):
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 7d218930d..7941ec5a1 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -884,18 +884,18 @@ Groß): > let h = '' for l in split(system('aiksaurus '.shellescape(a:base)), '\n') if l[:3] == '=== ' - let h = substitute(l[4:], ' =*$', '', '') - elseif l[0] =~ '\a' - call extend(res, map(split(l, ', '), {_, val -> {'word': val, 'menu': '('.h.')'}})) + let h = '('.substitute(l[4:], ' =*$', ')', '') + elseif l ==# 'Alphabetically similar known words are: ' + let h = '🔮' + elseif l[0] =~ '\a' || (h ==# '🔮' && l[0] ==# "\t") + call extend(res, map(split(substitute(l, '^\t', '', ''), ', '), {_, val -> {'word': val, 'menu': h}}))
endif endfor return res endif endfunc
- if exists('+thesaurusfunc') - set thesaurusfunc=Thesaur - endif +set thesaurusfunc=Thesaur Completing keywords in the current and included files *compl-keyword*
—
You are receiving this because you commented.
Could you make it into a separate plugin, so we have issue tracker, pull requests, etc., please?
I am not sure if this little snippet is worth it to create an extra repo for it. But I do see that in f4d8b76 this snippet was added to the official vim help, so maybe Bram could update it there.
The diff would be (it also includes a small optimization):diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 7d218930d..7941ec5a1 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -884,18 +884,18 @@ Groß): > let h = '' for l in split(system('aiksaurus '.shellescape(a:base)), '\n') if l[:3] == '=== ' - let h = substitute(l[4:], ' =*$', '', '') - elseif l[0] =~ '\a' - call extend(res, map(split(l, ', '), {_, val -> {'word': val, 'menu': '('.h.')'}})) + let h = '('.substitute(l[4:], ' =*$', ')', '') + elseif l ==# 'Alphabetically similar known words are: ' + let h = '🔮'
This block:
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '\a'
let start -= 1
endwhile
return start
Can be simplified into:
return searchpos('\<', 'bnW', line('.'))[1] - 1
And in this line:
for l in split(system('aiksaurus '.shellescape(a:base)), '\n')
split() can be removed by replacing system() with systemlist():
for l in systemlist('aiksaurus ' .. shellescape(a:base))
FWIW, this is what I use:
vim9script if !executable('aiksaurus') echomsg "'thesaurusfunc' needs the package aiksaurus to be installed" var pass: string = inputsecret('[sudo] password for ' .. $USER .. ': ') system('sudo -S apt-get --yes install aiksaurus', pass .. "\n") endif &thesaurusfunc = 'Thesaurusfunc' def g:Thesaurusfunc(findstart: bool, base: string): any if findstart return searchpos('\<', 'bnW', line('.'))[1] - 1 endif var lines: list<string> = systemlist('aiksaurus ' .. shellescape(base)) if lines[0] =~ 'No synonyms known' return [] endif var matches: list<dict<string>> var meaning: string for line: string in lines if line =~ '^===' meaning = line->matchstr('\k\+') elseif line =~ '^\k' var match: list<dict<string>> = line ->split(', ') ->mapnew((_, v: string): dict<string> => ({ word: v, menu: '(' .. meaning .. ')' })) matches->extend(match) endif endfor return matches enddef
—
You are receiving this because you commented.
Thanks both of you, these are indeed useful additions. Here is an updated diff for Bram to include in the next runtime update:
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 7d218930d..708b0049c 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -873,20 +873,17 @@ Groß): > func Thesaur(findstart, base) if a:findstart - let line = getline('.') - let start = col('.') - 1 - while start > 0 && line[start - 1] =~ '\a' - let start -= 1 - endwhile - return start + return searchpos('\<', 'bnW', line('.'))[1] - 1
else let res = [] let h = ''
- for l in split(system('aiksaurus '.shellescape(a:base)), '\n') + for l in systemlist('aiksaurus '.shellescape(a:base))
if l[:3] == '=== '
- let h = substitute(l[4:], ' =*$', '', '')
- elseif l[0] =~ '\a'
- call extend(res, map(split(l, ', '), {_, val -> {'word': val, 'menu': '('.h.')'}}))
+ let h = '('.substitute(l[4:], ' =*$', ')', '') + elseif l ==# 'Alphabetically similar known words are: ' + let h = "\U0001f52e" + elseif l[0] =~ '\a' || (h ==# "\U0001f52e" && l[0] ==# "\t") + call extend(res, map(split(substitute(l, '^\t', '', ''), ', '), {_, val -> {'word': val, 'menu': h}})) endif endfor return res
I have also updated the snippets above.
—
You are receiving this because you commented.
Thanks. I'll also reduce the indent to avoid wrapping.
—
You are receiving this because you commented.
I'll also reduce the indent to avoid wrapping.
To further reduce the indent, the else statement can be removed.
Before:
if condition return value else do thing 1 do thing 2 do thing 3 ... return other_value endif
After:
if condition return value endif do thing 1 do thing 2 do thing 3 ... return other_value
—
You are receiving this because you commented.