Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into feat/docs-preview-w…
Browse files Browse the repository at this point in the history
…indow

* upstream/main:
  fix: remove usages from vim.lsp.util.parse_snippet (hrsh7th#1734)
  ci: fix broken tests (hrsh7th#1729)
  Format with stylua
  docs: Add comments and type annotations for sorting comparators (hrsh7th#1687)
  fix(ghost_text): `ephemeral` causes a false positive of no inline support (hrsh7th#1645)
  perf(core): simplify and improve `find_line_suffix()` (hrsh7th#1675)
  Format with stylua
  feat: add toggle_doc functionality (hrsh7th#1647)
  add context check for invalid detection
  implement is_invalid detection
  perf: fix `nvim_replace_termcodes` being called on every `CursorMoved` (hrsh7th#1650)
  improve pattern handling
  Format with stylua
  fix confirm resolve
  • Loading branch information
williamboman committed Oct 26, 2023
2 parents 20b696c + 51260c0 commit 0ece15a
Show file tree
Hide file tree
Showing 17 changed files with 248 additions and 58 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
doc/tags
utils/stylua
.DS_Store

57 changes: 57 additions & 0 deletions doc/cmp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ NOTE: `<Cmd>lua require('cmp').complete()<CR>` can be used to call these functio
*cmp.visible* ()
Return a boolean showing whether the completion menu is visible or not.

*cmp.visible_docs* ()
Return a boolean showing whether the docs window is visible or not.

*cmp.get_entries* ()
Return all current entries.

Expand Down Expand Up @@ -197,6 +200,11 @@ NOTE: `<Cmd>lua require('cmp').complete()<CR>` can be used to call these functio
}
}
<
*cmp.open_docs* ()
Open docs view.

*cmp.close_docs* ()
Close docs view.

*cmp.open_docs_preview*
Opens the docs window (if visible) in a preview window.
Expand Down Expand Up @@ -341,6 +349,12 @@ There are also builtin mapping helper functions you can use:
*cmp.mapping.open_docs_preview*
Same as |cmp.open_docs_preview|.

*cmp.mapping.open_docs* ()
Same as |cmp.open_docs|.

*cmp.mapping.close_docs* ()
Same as |cmp.close_docs|.

*cmp.mapping.scroll_docs* (delta: number)
Same as |cmp.scroll_docs|.

Expand Down Expand Up @@ -446,6 +460,11 @@ performance.fetching_timeout~
Sets the timeout of candidate fetching process.
The nvim-cmp will wait to display the most prioritized source.

*cmp-config.performance.confirm_resolve_timeout*
performance.confirm_resolve_timeout~
`number`
Sets the timeout for resolving item before confirmation.

*cmp-config.performance.async_budget*
performance.async_budget~
`number`
Expand Down Expand Up @@ -660,6 +679,12 @@ view~
The view class used to customize nvim-cmp's appearance.
Currently available configuration options are:

*cmp-config.view.docs.auto_open*
view.docs.auto_open~
`boolean`

Specify whether to show the docs_view when selecting an item.

*cmp-config.window.{completion,documentation}.border*
window.{completion,documentation}.border~
`string | string[] | nil`
Expand Down Expand Up @@ -901,6 +926,38 @@ How to disable commitCharacters?~
}
<

How to disable automatic display of docs view?~

You can add the `view.docs.auto_open = false` for configuration.
>lua
cmp.setup {
...
view = {
docs = {
auto_open = false
}
}
...
}
<

additionaly, if you want to open/close docs view via your key mapping, you
can define keymapping as the following.
>lua
cmp.setup {
...
mapping = {
['<C-g>'] = function()
if cmp.visible_docs() then
cmp.close_docs()
else
cmp.open_docs()
end
end
}
...
}
<

How to disable auto-completion?~
How to use nvim-cmp as omnifunc?~
Expand Down
60 changes: 48 additions & 12 deletions lua/cmp/config/compare.lua
Original file line number Diff line number Diff line change
@@ -1,56 +1,79 @@
local types = require('cmp.types')
local cache = require('cmp.utils.cache')

---@type cmp.Comparator[]
local compare = {}

-- offset
--- Comparators (:help cmp-config.sorting.comparators) should return
--- true when the first entry should come EARLIER (i.e., higher ranking) than the second entry,
--- or nil if no pairwise ordering preference from the comparator.
--- See also :help table.sort() and cmp.view.open() to see how comparators are used.

---@class cmp.ComparatorFunctor
---@overload fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil
---@alias cmp.ComparatorFunction fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil
---@alias cmp.Comparator cmp.ComparatorFunction | cmp.ComparatorFunctor

---offset: Entries with smaller offset will be ranked higher.
---@type cmp.ComparatorFunction
compare.offset = function(entry1, entry2)
local diff = entry1:get_offset() - entry2:get_offset()
if diff < 0 then
return true
elseif diff > 0 then
return false
end
return nil
end

-- exact
---exact: Entries with exact == true will be ranked higher.
---@type cmp.ComparatorFunction
compare.exact = function(entry1, entry2)
if entry1.exact ~= entry2.exact then
return entry1.exact
end
return nil
end

-- score
---score: Entries with higher score will be ranked higher.
---@type cmp.ComparatorFunction
compare.score = function(entry1, entry2)
local diff = entry2.score - entry1.score
if diff < 0 then
return true
elseif diff > 0 then
return false
end
return nil
end

-- recently_used
---recently_used: Entries that are used recently will be ranked higher.
---@type cmp.ComparatorFunctor
compare.recently_used = setmetatable({
records = {},
add_entry = function(self, e)
self.records[e.completion_item.label] = vim.loop.now()
end,
}, {
---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
__call = function(self, entry1, entry2)
local t1 = self.records[entry1.completion_item.label] or -1
local t2 = self.records[entry2.completion_item.label] or -1
if t1 ~= t2 then
return t1 > t2
end
return nil
end,
})

-- kind
---kind: Entires with smaller ordinal value of 'kind' will be ranked higher.
---(see lsp.CompletionItemKind enum).
---Exceptions are that Text(1) will be ranked the lowest, and snippets be the highest.
---@type cmp.ComparatorFunction
compare.kind = function(entry1, entry2)
local kind1 = entry1:get_kind()
local kind1 = entry1:get_kind() --- @type lsp.CompletionItemKind | number
local kind2 = entry2:get_kind() --- @type lsp.CompletionItemKind | number
kind1 = kind1 == types.lsp.CompletionItemKind.Text and 100 or kind1
local kind2 = entry2:get_kind()
kind2 = kind2 == types.lsp.CompletionItemKind.Text and 100 or kind2
if kind1 ~= kind2 then
if kind1 == types.lsp.CompletionItemKind.Snippet then
Expand All @@ -66,9 +89,11 @@ compare.kind = function(entry1, entry2)
return false
end
end
return nil
end

-- sortText
---sort_text: Entries will be ranked according to the lexicographical order of sortText.
---@type cmp.ComparatorFunction
compare.sort_text = function(entry1, entry2)
if entry1.completion_item.sortText and entry2.completion_item.sortText then
local diff = vim.stricmp(entry1.completion_item.sortText, entry2.completion_item.sortText)
Expand All @@ -78,29 +103,36 @@ compare.sort_text = function(entry1, entry2)
return false
end
end
return nil
end

-- length
---length: Entires with shorter label length will be ranked higher.
---@type cmp.ComparatorFunction
compare.length = function(entry1, entry2)
local diff = #entry1.completion_item.label - #entry2.completion_item.label
if diff < 0 then
return true
elseif diff > 0 then
return false
end
return nil
end

-- order
----order: Entries with smaller id will be ranked higher.
---@type fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
compare.order = function(entry1, entry2)
local diff = entry1.id - entry2.id
if diff < 0 then
return true
elseif diff > 0 then
return false
end
return nil
end

-- locality
---locality: Entries with higher locality (i.e., words that are closer to the cursor)
---will be ranked higher. See GH-183 for more details.
---@type cmp.ComparatorFunctor
compare.locality = setmetatable({
lines_count = 10,
lines_cache = cache.new(),
Expand Down Expand Up @@ -146,6 +178,7 @@ compare.locality = setmetatable({
end
end,
}, {
---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
__call = function(self, entry1, entry2)
local local1 = self.locality_map[entry1:get_word()]
local local2 = self.locality_map[entry2:get_word()]
Expand All @@ -158,10 +191,12 @@ compare.locality = setmetatable({
end
return local1 < local2
end
return nil
end,
})

-- scopes
---scopes: Entries defined in a closer scope will be ranked higher (e.g., prefer local variables to globals).
---@type cmp.ComparatorFunctor
compare.scopes = setmetatable({
scopes_map = {},
update = function(self)
Expand Down Expand Up @@ -216,6 +251,7 @@ compare.scopes = setmetatable({
end
end,
}, {
---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
__call = function(self, entry1, entry2)
local local1 = self.scopes_map[entry1:get_word()]
local local2 = self.scopes_map[entry2:get_word()]
Expand Down
4 changes: 4 additions & 0 deletions lua/cmp/config/default.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ return function()
debounce = 60,
throttle = 30,
fetching_timeout = 500,
confirm_resolve_timeout = 80,
async_budget = 1,
max_view_entries = 200,
},
Expand Down Expand Up @@ -94,6 +95,9 @@ return function()
name = 'custom',
selection_order = 'top_down',
},
docs = {
auto_open = true,
},
},

window = {
Expand Down
19 changes: 19 additions & 0 deletions lua/cmp/config/mapping.lua
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,25 @@ mapping.open_docs_preview = function()
end
end

--- Opens the documentation window.
mapping.open_docs = function()
return function(fallback)
if not require('cmp').open_docs() then
fallback()
end
end
end

--- Close the documentation window.
mapping.close_docs = function()
return function(fallback)
if not require('cmp').close_docs() then
>>>>>>> upstream/main
fallback()
end
end
end

---Select next completion item.
mapping.select_next_item = function(option)
return function(fallback)
Expand Down
2 changes: 1 addition & 1 deletion lua/cmp/context.lua
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ end
---@return integer
context.get_offset = function(self, keyword_pattern)
return self.cache:ensure({ 'get_offset', keyword_pattern, self.cursor_before_line }, function()
return pattern.offset(keyword_pattern .. '\\m$', self.cursor_before_line) or self.cursor.col
return pattern.offset([[\%(]] .. keyword_pattern .. [[\)\m$]], self.cursor_before_line) or self.cursor.col
end)
end

Expand Down
21 changes: 11 additions & 10 deletions lua/cmp/core.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
local debug = require('cmp.utils.debug')
local str = require('cmp.utils.str')
local char = require('cmp.utils.char')
local pattern = require('cmp.utils.pattern')
local feedkeys = require('cmp.utils.feedkeys')
local async = require('cmp.utils.async')
local keymap = require('cmp.utils.keymap')
Expand Down Expand Up @@ -190,16 +189,14 @@ core.on_moved = function(self)
self:filter()
end

-- Find the suffix for the specified line
---Returns the suffix of the specified `line`.
---
---Contains `%s`: returns everything after the last `%s` in `line`
---Else: returns `line` unmodified
---@param line string
---@return string suffix
local function find_line_suffix(line)
local i = #line
while i > 0 do
if line:sub(i, i):find('%s') then
return line:sub(i + 1)
end
i = i - 1
end
return line
return line:match('%S*$') --[[@as string]]
end

---Check autoindent
Expand Down Expand Up @@ -363,6 +360,10 @@ core.confirm = function(self, e, option, callback)

debug.log('entry.confirm', e:get_completion_item())

async.sync(function(done)
e:resolve(done)
end, config.get().performance.confirm_resolve_timeout)

local release = self:suspend()

-- Close menus.
Expand Down
6 changes: 3 additions & 3 deletions lua/cmp/core_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,11 @@ describe('cmp.core', function()
character = 6,
},
},
newText = 'foobarbaz',
newText = 'AIUEO',
},
})
assert.are.same(state.buffer, { '***foobarbaz***' })
assert.are.same(state.cursor[2], 12)
assert.are.same(state.buffer, { '***AIUEO***' })
assert.are.same(state.cursor[2], 6)
end)
end)
end)
Expand Down
Loading

0 comments on commit 0ece15a

Please sign in to comment.