Dictionary source for blink.cmp completion plugin. This makes it possible to query a dictionary without leaving the editor.
Fuzzy finding is supported by default:
Definitions of words are also supported (use wn
by default):
Note
wn
is the abbreviation of WordNet
, which is a lexical database of the English language.
If you don't know how to install wn
, you may google the keyword
how to install WordNet on ...
.
For the default configuration, you must have fzf
(or rg
) to search in the dictionary file.
And wn
must be installed to get the definitions of words. cat
for concatenating the dictionary
files. You can use checkhealth blink-cmp-dictionary
to check if the requirements are met.
If you have no cat
in your system,
see How to customize the command.
Add the plugin to your packer managers, and make sure it is loaded before blink.cmp
.
{
'saghen/blink.cmp',
dependencies = {
{
'Kaiser-Yang/blink-cmp-dictionary',
dependencies = { 'nvim-lua/plenary.nvim' }
}
-- ... Other dependencies
},
opts = {
sources = {
-- Add 'dictionary' to the list
default = { 'dictionary', 'lsp', 'path', 'luasnip', 'buffer' },
providers = {
dictionary = {
module = 'blink-cmp-dictionary',
name = 'Dict',
-- Make sure this is at least 2.
-- 3 is recommended
min_keyword_length = 3,
opts = {
-- options for blink-cmp-dictionary
}
}
},
}
}
}
Note
If you don't have a dictionary file, see english-words.
By default, your dictionary files must be like this content (every line is a word):
word1
word2
If you dictionary files are like these. You just need to specify the dictionary files' path in the configuration:
-- Specify the dictionary files' path
-- example: { vim.fn.expand('~/.config/nvim/dictionary/words.dict') }
dictionary_files = nil,
-- All .txt files in these directories will be treated as dictionary files
-- example: { vim.fn.expand('~/.config/nvim/dictionary') }
dictionary_directories = nil,
Note
All the dictionary files in dictionary_files
and dictionary_directories
will be
concatenated by cat
command. Make sure the files are different, otherwise there will be
duplicate words in the completion list. If your dictionary files are not separated by lines,
see How to customize completion items
See default.lua.
You just need use a function to determine the dictionary files for different file types, for example:
dictionary_files = function()
if vim.bo.filetype == 'markdown' then
return { vim.fn.expand('~/.config/nvim/dictionary/markdown.dict') }
end
return { vim.fn.expand('~/.config/nvim/dictionary/words.dict') }
end,
In blink-cmp-dictionary
we use get_prefix
to determine which part to search. If we do not use
fzf
, for example we use rg
, and we set min_keyword_length=3
. After inputting 'dic',
blink.cmp
will get all the words that start with 'dic', then blink.cmp
will fuzzy find on
words starting with 'dic'. The process makes it impossible to complete 'dictionary'
when inputting 'dit'. But if we use fzf
, fzf
will return 'dictionary' when inputting dit
('dit' is a sub-sequence of 'dictionary'). So the fuzzy finding feature are fully supported.
By default, blink-cmp-dictionary
treat every line in the dictionary files as a completion item.
You can update this by use separate_output
in the configuration:
separate_output = function(output)
local items = {}
-- You may need to change the pattern to match your dictionary files
for line in output:gmatch("[^\r\n]+") do
local items = {}
for line in output:gmatch("[^\r\n]+") do
table.insert(items, line)
end
return items
end
return items
end
After calling separate_output
, blink-cmp-dictionary
will call get_label
, get_insert_text
,
get_documentation
, and get_kind
for each item in the list to assemble the completion items.
Those below are the default:
get_label = function(item)
return item
end,
get_insert_text = function(item)
return item
end,
get_kind_name = function(_)
return 'Dict'
end,
get_documentation = function(item)
-- use return nil to disable the documentation
-- return nil
return {
get_command = function()
return utils.command_found('wn') and 'wn' or ''
end,
get_command_args = function()
return { item, '-over' }
end,
resolve_documentation = function(output)
return output
end,
on_error = default_on_error,
}
end,
By default, blink-cmp-dictionary
will use fzf
to read from the output of cat
. If you do not
have cat
in your system, you may have other commands to output the content of files, just create
a symbolic link named cat
to the command you use.
You may configure a new command which supports reading from files directly, for example, rg
:
-- set them with nil to pass files directly to the command
dictionary_files = nil,
dictionary_directories = nil,
get_command = 'rg',
get_command_args = function(prefix, _)
local dictionary_file1 = 'path/to/your/dictionary/file1'
local dictionary_file2 = 'path/to/your/dictionary/file2'
return {
'--color=never',
'--no-line-number',
'--no-messages',
'--no-filename',
'--smart-case',
'--',
prefix,
-- pass the dictionary files to the command
dictionary_file1,
dictionary_file2,
}
end
If you just want to customize the arguments for fzf
, for example,
those below will ignore the case:
get_command_args = function(prefix, _)
return {
'--filter=' .. prefix,
'--sync',
'--no-sort',
'-i' -- -i to ignore case, +i to respect case, with no this line is smart case
}
end,
Customize the BlinkCmpKindDict
to customize the highlight for kind icon, here is an example:
vim.api.nvim_set_hl(0, 'BlinkCmpKindDict', { default = false, fg = '#a6e3a1' })
Update the default
of blink.cmp
:
-- Use this function to check if the cursor is inside a comment block
local function inside_comment_block()
if vim.api.nvim_get_mode().mode ~= 'i' then
return false
end
local node_under_cursor = vim.treesitter.get_node()
local parser = vim.treesitter.get_parser(nil, nil, { error = false })
local query = vim.treesitter.query.get(vim.bo.filetype, 'highlights')
if not parser or not node_under_cursor or not query then
return false
end
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
row = row - 1
for id, node, _ in query:iter_captures(node_under_cursor, 0, row, row + 1) do
if query.captures[id]:find('comment') then
local start_row, start_col, end_row, end_col = node:range()
if start_row <= row and row <= end_row then
if start_row == row and end_row == row then
if start_col <= col and col <= end_col then
return true
end
elseif start_row == row then
if start_col <= col then
return true
end
elseif end_row == row then
if col <= end_col then
return true
end
else
return true
end
end
end
end
return false
end
-- this is the opts for blink.cmp
---@module 'blink.cmp'
---@type blink.cmp.Config
opts = {
sources = {
default = function()
-- put those which will be shown always
local result = {'lsp', 'path', 'luasnip', 'buffer' }
if
-- turn on dictionary in markdown or text file
vim.tbl_contains({ 'markdown', 'text' }, vim.bo.filetype) or
-- or turn on dictionary if cursor is in the comment block
inside_comment_block()
then
table.insert(result, 'dictionary')
end
return result
end,
}
}
blink-cmp-dictionary
is asynchronous by default, so it should not block other operations.
But there are something you should note:
- Make sure the
min_keyword_length
is at least 2. If your dictionary files are very large, a larger value is recommended. This is mainly becauseblink-cmp-dictionary
actually can handle this quickly, but there will be too many results return toblink.cmp
, which will makeblink.cmp
take a long time to fuzzy find the results. - Optionally, you can limit the number of items shown in the completion menu.
opts = {
sources = {
providers = {
dictionary = {
-- Add this and change the value to your own preference
max_items = 8,
}
},
}
}
The release versions are something like major.minor.patch
. When one of these numbers is increased:
patch
: bugs are fixed or docs are added. This will not break the compatibility.minor
: compatible features are added. This may cause some configurationsdeprecated
, but not break the compatibility.major
: incompatible features are added. All thedeprecated
configurations will be removed. This will break the compatibility.
Nice and fast completion plugin: blink.cmp.
Inspired by cmp-dictionary.
Learned how to write a source from blink-ripgrep.nvim.