mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 12:45:17 -07:00
Compare commits
19 Commits
c0c283968c
...
2ba46fcdd8
Author | SHA1 | Date | |
---|---|---|---|
|
2ba46fcdd8 | ||
|
02bc40c194 | ||
|
160cbd0ef4 | ||
|
35cea0b28d | ||
|
b037af3f2c | ||
|
3db3947b0e | ||
|
fab5cbad00 | ||
|
a61c1eee21 | ||
|
be6b7cdc5c | ||
|
e007e977d2 | ||
|
7d2d0a5549 | ||
|
1e42a0e748 | ||
|
fb58bc8da5 | ||
|
b730a4c625 | ||
|
f94e98556e | ||
|
112a5cf679 | ||
|
d43690d82a | ||
|
260b4bb3a5 | ||
|
d2ce9e21c8 |
@ -60,7 +60,7 @@ hi('PmenuMatch', { link = 'Pmenu' })
|
|||||||
hi('PmenuMatchSel', { link = 'PmenuSel' })
|
hi('PmenuMatchSel', { link = 'PmenuSel' })
|
||||||
hi('PmenuExtra', { link = 'Pmenu' })
|
hi('PmenuExtra', { link = 'Pmenu' })
|
||||||
hi('PmenuExtraSel', { link = 'PmenuSel' })
|
hi('PmenuExtraSel', { link = 'PmenuSel' })
|
||||||
hi('ComplMatchIns', { link = 'Normal' })
|
hi('ComplMatchIns', {})
|
||||||
hi('Substitute', { link = 'Search' })
|
hi('Substitute', { link = 'Search' })
|
||||||
hi('Whitespace', { link = 'NonText' })
|
hi('Whitespace', { link = 'NonText' })
|
||||||
hi('MsgSeparator', { link = 'StatusLine' })
|
hi('MsgSeparator', { link = 'StatusLine' })
|
||||||
|
@ -2823,11 +2823,12 @@ nvim_set_decoration_provider({ns_id}, {opts})
|
|||||||
Note: this function should not be called often. Rather, the callbacks
|
Note: this function should not be called often. Rather, the callbacks
|
||||||
themselves can be used to throttle unneeded callbacks. the `on_start`
|
themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||||
callback can return `false` to disable the provider until the next redraw.
|
callback can return `false` to disable the provider until the next redraw.
|
||||||
Similarly, return `false` in `on_win` will skip the `on_line` calls for
|
Similarly, return `false` in `on_win` will skip the `on_line` and
|
||||||
that window (but any extmarks set in `on_win` will still be used). A
|
`on_range` calls for that window (but any extmarks set in `on_win` will
|
||||||
plugin managing multiple sources of decoration should ideally only set one
|
still be used). A plugin managing multiple sources of decoration should
|
||||||
provider, and merge the sources internally. You can use multiple `ns_id`
|
ideally only set one provider, and merge the sources internally. You can
|
||||||
for the extmarks set/modified inside the callback anyway.
|
use multiple `ns_id` for the extmarks set/modified inside the callback
|
||||||
|
anyway.
|
||||||
|
|
||||||
Note: doing anything other than setting extmarks is considered
|
Note: doing anything other than setting extmarks is considered
|
||||||
experimental. Doing things like changing options are not explicitly
|
experimental. Doing things like changing options are not explicitly
|
||||||
@ -2835,8 +2836,8 @@ nvim_set_decoration_provider({ns_id}, {opts})
|
|||||||
consumption). Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is
|
consumption). Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is
|
||||||
quite dubious for the moment.
|
quite dubious for the moment.
|
||||||
|
|
||||||
Note: It is not allowed to remove or update extmarks in `on_line`
|
Note: It is not allowed to remove or update extmarks in `on_line` and
|
||||||
callbacks.
|
`on_range` callbacks.
|
||||||
|
|
||||||
Attributes: ~
|
Attributes: ~
|
||||||
Lua |vim.api| only
|
Lua |vim.api| only
|
||||||
@ -2857,6 +2858,14 @@ nvim_set_decoration_provider({ns_id}, {opts})
|
|||||||
• on_line: called for each buffer line being redrawn. (The
|
• on_line: called for each buffer line being redrawn. (The
|
||||||
interaction with fold lines is subject to change) >
|
interaction with fold lines is subject to change) >
|
||||||
["line", winid, bufnr, row]
|
["line", winid, bufnr, row]
|
||||||
|
<
|
||||||
|
• on_range: called for each buffer range being redrawn. Range
|
||||||
|
is end-exclusive and may span multiple lines. Range bounds
|
||||||
|
point to the first byte of a character. An end position of
|
||||||
|
the form (lnum, 0), including (number of lines, 0), is
|
||||||
|
valid and indicates that EOL of the preceding line is
|
||||||
|
included. >
|
||||||
|
["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
|
||||||
<
|
<
|
||||||
• on_end: called at the end of a redraw cycle >
|
• on_end: called at the end of a redraw cycle >
|
||||||
["end", tick]
|
["end", tick]
|
||||||
|
@ -178,6 +178,7 @@ API
|
|||||||
|
|
||||||
• |nvim__ns_set()| can set properties for a namespace
|
• |nvim__ns_set()| can set properties for a namespace
|
||||||
• |vim.json.encode()| has an option to enable forward slash escaping
|
• |vim.json.encode()| has an option to enable forward slash escaping
|
||||||
|
• Decoration provider has `on_range()` callback.
|
||||||
|
|
||||||
DEFAULTS
|
DEFAULTS
|
||||||
|
|
||||||
@ -303,6 +304,7 @@ TREESITTER
|
|||||||
• |treesitter-directive-trim!| can trim all whitespace (not just empty lines)
|
• |treesitter-directive-trim!| can trim all whitespace (not just empty lines)
|
||||||
from both sides of a node.
|
from both sides of a node.
|
||||||
• |vim.treesitter.get_captures_at_pos()| now returns the `id` of each capture
|
• |vim.treesitter.get_captures_at_pos()| now returns the `id` of each capture
|
||||||
|
• |Query:iter_captures()| supports specifying starting and ending columns.
|
||||||
|
|
||||||
TUI
|
TUI
|
||||||
|
|
||||||
|
@ -1348,7 +1348,7 @@ parse({lang}, {query}) *vim.treesitter.query.parse()*
|
|||||||
• |vim.treesitter.query.get()|
|
• |vim.treesitter.query.get()|
|
||||||
|
|
||||||
*Query:iter_captures()*
|
*Query:iter_captures()*
|
||||||
Query:iter_captures({node}, {source}, {start}, {stop})
|
Query:iter_captures({node}, {source}, {start}, {stop}, {opts})
|
||||||
Iterate over all captures from all matches inside {node}
|
Iterate over all captures from all matches inside {node}
|
||||||
|
|
||||||
{source} is needed if the query contains predicates; then the caller must
|
{source} is needed if the query contains predicates; then the caller must
|
||||||
@ -1382,9 +1382,11 @@ Query:iter_captures({node}, {source}, {start}, {stop})
|
|||||||
`node:start()`.
|
`node:start()`.
|
||||||
• {stop} (`integer?`) Stopping line for the search (end-exclusive).
|
• {stop} (`integer?`) Stopping line for the search (end-exclusive).
|
||||||
Defaults to `node:end_()`.
|
Defaults to `node:end_()`.
|
||||||
|
• {opts} (`{ col_begin?: integer, col_end?: integer }?`) Optional
|
||||||
|
parameters.
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
(`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch`)
|
(`fun(end_line: integer?, end_col: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch`)
|
||||||
capture id, capture node, metadata, match
|
capture id, capture node, metadata, match
|
||||||
|
|
||||||
*Query:iter_matches()*
|
*Query:iter_matches()*
|
||||||
|
15
runtime/lua/vim/_meta/api.lua
generated
15
runtime/lua/vim/_meta/api.lua
generated
@ -2114,8 +2114,8 @@ function vim.api.nvim_set_current_win(window) end
|
|||||||
--- Note: this function should not be called often. Rather, the callbacks
|
--- Note: this function should not be called often. Rather, the callbacks
|
||||||
--- themselves can be used to throttle unneeded callbacks. the `on_start`
|
--- themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||||
--- callback can return `false` to disable the provider until the next redraw.
|
--- callback can return `false` to disable the provider until the next redraw.
|
||||||
--- Similarly, return `false` in `on_win` will skip the `on_line` calls
|
--- Similarly, return `false` in `on_win` will skip the `on_line` and `on_range`
|
||||||
--- for that window (but any extmarks set in `on_win` will still be used).
|
--- calls for that window (but any extmarks set in `on_win` will still be used).
|
||||||
--- A plugin managing multiple sources of decoration should ideally only set
|
--- A plugin managing multiple sources of decoration should ideally only set
|
||||||
--- one provider, and merge the sources internally. You can use multiple `ns_id`
|
--- one provider, and merge the sources internally. You can use multiple `ns_id`
|
||||||
--- for the extmarks set/modified inside the callback anyway.
|
--- for the extmarks set/modified inside the callback anyway.
|
||||||
@ -2126,7 +2126,8 @@ function vim.api.nvim_set_current_win(window) end
|
|||||||
--- Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
|
--- Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
|
||||||
--- for the moment.
|
--- for the moment.
|
||||||
---
|
---
|
||||||
--- Note: It is not allowed to remove or update extmarks in `on_line` callbacks.
|
--- Note: It is not allowed to remove or update extmarks
|
||||||
|
--- in `on_line` and `on_range` callbacks.
|
||||||
---
|
---
|
||||||
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
|
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
|
||||||
--- @param opts vim.api.keyset.set_decoration_provider Table of callbacks:
|
--- @param opts vim.api.keyset.set_decoration_provider Table of callbacks:
|
||||||
@ -2148,6 +2149,14 @@ function vim.api.nvim_set_current_win(window) end
|
|||||||
--- ```
|
--- ```
|
||||||
--- ["line", winid, bufnr, row]
|
--- ["line", winid, bufnr, row]
|
||||||
--- ```
|
--- ```
|
||||||
|
--- - on_range: called for each buffer range being redrawn.
|
||||||
|
--- Range is end-exclusive and may span multiple lines. Range
|
||||||
|
--- bounds point to the first byte of a character. An end position
|
||||||
|
--- of the form (lnum, 0), including (number of lines, 0), is valid
|
||||||
|
--- and indicates that EOL of the preceding line is included.
|
||||||
|
--- ```
|
||||||
|
--- ["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
|
||||||
|
--- ```
|
||||||
--- - on_end: called at the end of a redraw cycle
|
--- - on_end: called at the end of a redraw cycle
|
||||||
--- ```
|
--- ```
|
||||||
--- ["end", tick]
|
--- ["end", tick]
|
||||||
|
1
runtime/lua/vim/_meta/api_keysets.lua
generated
1
runtime/lua/vim/_meta/api_keysets.lua
generated
@ -231,6 +231,7 @@ error('Cannot require a meta file')
|
|||||||
--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer)
|
--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer)
|
||||||
--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer)
|
--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer)
|
||||||
--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer)
|
--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer)
|
||||||
|
--- @field on_range? fun(_: "range", winid: integer, bufnr: integer, row_begin: integer, col_begin: integer, row_end: integer, col_end: integer)
|
||||||
--- @field on_end? fun(_: "end", tick: integer)
|
--- @field on_end? fun(_: "end", tick: integer)
|
||||||
--- @field _on_hl_def? fun(_: "hl_def")
|
--- @field _on_hl_def? fun(_: "hl_def")
|
||||||
--- @field _on_spell_nav? fun(_: "spell_nav")
|
--- @field _on_spell_nav? fun(_: "spell_nav")
|
||||||
|
@ -71,8 +71,6 @@ function TSQueryCursor:next_match() end
|
|||||||
|
|
||||||
--- @param node TSNode
|
--- @param node TSNode
|
||||||
--- @param query TSQuery
|
--- @param query TSQuery
|
||||||
--- @param start integer?
|
--- @param opts? { row_begin?: integer, col_begin?: integer, row_end?: integer, col_end?: integer, max_start_depth?: integer, match_limit?: integer }
|
||||||
--- @param stop integer?
|
|
||||||
--- @param opts? { max_start_depth?: integer, match_limit?: integer}
|
|
||||||
--- @return TSQueryCursor
|
--- @return TSQueryCursor
|
||||||
function vim._create_ts_querycursor(node, query, start, stop, opts) end
|
function vim._create_ts_querycursor(node, query, opts) end
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
local api = vim.api
|
local api = vim.api
|
||||||
local query = vim.treesitter.query
|
local query = vim.treesitter.query
|
||||||
local Range = require('vim.treesitter._range')
|
local Range = require('vim.treesitter._range')
|
||||||
|
local cmp_eq = Range.cmp_pos.eq
|
||||||
|
local cmp_lt = Range.cmp_pos.lt
|
||||||
|
local cmp_lte = Range.cmp_pos.le
|
||||||
|
|
||||||
local ns = api.nvim_create_namespace('treesitter/highlighter')
|
local ns = api.nvim_create_namespace('treesitter/highlighter')
|
||||||
|
|
||||||
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch
|
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil, end_col: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch
|
||||||
|
|
||||||
---@class (private) vim.treesitter.highlighter.Query
|
---@class (private) vim.treesitter.highlighter.Query
|
||||||
---@field private _query vim.treesitter.Query?
|
---@field private _query vim.treesitter.Query?
|
||||||
@ -55,6 +58,7 @@ end
|
|||||||
---@class (private) vim.treesitter.highlighter.State
|
---@class (private) vim.treesitter.highlighter.State
|
||||||
---@field tstree TSTree
|
---@field tstree TSTree
|
||||||
---@field next_row integer
|
---@field next_row integer
|
||||||
|
---@field next_col integer
|
||||||
---@field iter vim.treesitter.highlighter.Iter?
|
---@field iter vim.treesitter.highlighter.Iter?
|
||||||
---@field highlighter_query vim.treesitter.highlighter.Query
|
---@field highlighter_query vim.treesitter.highlighter.Query
|
||||||
|
|
||||||
@ -197,6 +201,7 @@ function TSHighlighter:prepare_highlight_states(srow, erow)
|
|||||||
table.insert(self._highlight_states, {
|
table.insert(self._highlight_states, {
|
||||||
tstree = tstree,
|
tstree = tstree,
|
||||||
next_row = 0,
|
next_row = 0,
|
||||||
|
next_col = 0,
|
||||||
iter = nil,
|
iter = nil,
|
||||||
highlighter_query = highlighter_query,
|
highlighter_query = highlighter_query,
|
||||||
})
|
})
|
||||||
@ -278,86 +283,119 @@ end
|
|||||||
|
|
||||||
---@param self vim.treesitter.highlighter
|
---@param self vim.treesitter.highlighter
|
||||||
---@param buf integer
|
---@param buf integer
|
||||||
---@param line integer
|
---@param range_br integer
|
||||||
|
---@param range_bc integer
|
||||||
|
---@param range_er integer
|
||||||
|
---@param range_ec integer
|
||||||
---@param is_spell_nav boolean
|
---@param is_spell_nav boolean
|
||||||
local function on_line_impl(self, buf, line, is_spell_nav)
|
local function on_range_impl(self, buf, range_br, range_bc, range_er, range_ec, is_spell_nav)
|
||||||
self:for_each_highlight_state(function(state)
|
self:for_each_highlight_state(function(state)
|
||||||
local root_node = state.tstree:root()
|
local root_node = state.tstree:root()
|
||||||
local root_start_row, _, root_end_row, _ = root_node:range()
|
local root_br, root_bc, root_er, root_ec = root_node:range()
|
||||||
|
|
||||||
-- Only consider trees that contain this line
|
--- @type boolean
|
||||||
if root_start_row > line or root_end_row < line then
|
local root_intersects
|
||||||
|
|
||||||
|
local range_empty = cmp_eq(range_br, range_bc, range_er, range_ec)
|
||||||
|
local root_empty = cmp_eq(root_br, root_bc, root_er, root_ec)
|
||||||
|
if not range_empty and not root_empty then
|
||||||
|
root_intersects = cmp_lt(range_br, range_bc, root_er, root_ec)
|
||||||
|
and cmp_lt(root_br, root_bc, range_er, range_ec)
|
||||||
|
else
|
||||||
|
root_intersects = cmp_lte(range_br, range_bc, root_er, root_ec)
|
||||||
|
and cmp_lte(root_br, root_bc, range_er, range_ec)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only consider trees within the visible range
|
||||||
|
if not root_intersects then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if state.iter == nil or state.next_row < line then
|
local next_row = state.next_row
|
||||||
|
local next_col = state.next_col
|
||||||
|
|
||||||
|
if state.iter == nil or cmp_lt(next_row, next_col, range_br, range_bc) then
|
||||||
-- Mainly used to skip over folds
|
-- Mainly used to skip over folds
|
||||||
|
|
||||||
-- TODO(lewis6991): Creating a new iterator loses the cached predicate results for query
|
-- TODO(lewis6991): Creating a new iterator loses the cached predicate results for query
|
||||||
-- matches. Move this logic inside iter_captures() so we can maintain the cache.
|
-- matches. Move this logic inside iter_captures() so we can maintain the cache.
|
||||||
state.iter =
|
state.iter = state.highlighter_query:query():iter_captures(
|
||||||
state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
|
root_node,
|
||||||
|
self.bufnr,
|
||||||
|
range_br,
|
||||||
|
root_er,
|
||||||
|
{ col_begin = range_bc, col_end = root_ec }
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
while line >= state.next_row do
|
while cmp_lt(next_row, next_col, range_er, range_ec) do
|
||||||
local capture, node, metadata, match = state.iter(line)
|
local capture, node, metadata, match = state.iter(range_er, range_ec)
|
||||||
|
if not node then
|
||||||
local range = { root_end_row + 1, 0, root_end_row + 1, 0 }
|
next_row = math.huge
|
||||||
if node then
|
next_col = math.huge
|
||||||
range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
|
||||||
local start_row, start_col, end_row, end_col = Range.unpack4(range)
|
local start_row, start_col, end_row, end_col = Range.unpack4(range)
|
||||||
|
|
||||||
if capture then
|
if cmp_lt(next_row, next_col, start_row, start_col) then
|
||||||
local hl = state.highlighter_query:get_hl_from_capture(capture)
|
next_row = start_row
|
||||||
|
next_col = start_col
|
||||||
local capture_name = state.highlighter_query:query().captures[capture]
|
|
||||||
|
|
||||||
local spell, spell_pri_offset = get_spell(capture_name)
|
|
||||||
|
|
||||||
-- The "priority" attribute can be set at the pattern level or on a particular capture
|
|
||||||
local priority = (
|
|
||||||
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
|
|
||||||
or vim.hl.priorities.treesitter
|
|
||||||
) + spell_pri_offset
|
|
||||||
|
|
||||||
-- The "conceal" attribute can be set at the pattern level or on a particular capture
|
|
||||||
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
|
|
||||||
|
|
||||||
local url = get_url(match, buf, capture, metadata)
|
|
||||||
|
|
||||||
if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
|
|
||||||
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
|
||||||
end_line = end_row,
|
|
||||||
end_col = end_col,
|
|
||||||
hl_group = hl,
|
|
||||||
ephemeral = true,
|
|
||||||
priority = priority,
|
|
||||||
conceal = conceal,
|
|
||||||
spell = spell,
|
|
||||||
url = url,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if start_row > line then
|
if not capture then
|
||||||
state.next_row = start_row
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local hl = state.highlighter_query:get_hl_from_capture(capture)
|
||||||
|
local capture_name = state.highlighter_query:query().captures[capture]
|
||||||
|
local spell, spell_pri_offset = get_spell(capture_name)
|
||||||
|
|
||||||
|
-- The "priority" attribute can be set at the pattern level or on a particular capture
|
||||||
|
local priority = (
|
||||||
|
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
|
||||||
|
or vim.hl.priorities.treesitter
|
||||||
|
) + spell_pri_offset
|
||||||
|
|
||||||
|
-- The "conceal" attribute can be set at the pattern level or on a particular capture
|
||||||
|
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
|
||||||
|
|
||||||
|
local url = get_url(match, buf, capture, metadata)
|
||||||
|
|
||||||
|
if not is_spell_nav or spell ~= nil then
|
||||||
|
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
||||||
|
end_line = end_row,
|
||||||
|
end_col = end_col,
|
||||||
|
ephemeral = true,
|
||||||
|
priority = priority,
|
||||||
|
hl_group = hl,
|
||||||
|
conceal = conceal,
|
||||||
|
spell = spell,
|
||||||
|
url = url,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
state.next_row = next_row
|
||||||
|
state.next_col = next_col
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param _win integer
|
---@param _win integer
|
||||||
---@param buf integer
|
---@param buf integer
|
||||||
---@param line integer
|
---@param br integer
|
||||||
function TSHighlighter._on_line(_, _win, buf, line, _)
|
---@param bc integer
|
||||||
|
---@param er integer
|
||||||
|
---@param ec integer
|
||||||
|
function TSHighlighter._on_range(_, _win, buf, br, bc, er, ec, _)
|
||||||
local self = TSHighlighter.active[buf]
|
local self = TSHighlighter.active[buf]
|
||||||
if not self then
|
if not self then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
on_line_impl(self, buf, line, false)
|
on_range_impl(self, buf, br, bc, er, ec, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@ -375,9 +413,7 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _)
|
|||||||
local highlight_states = self._highlight_states
|
local highlight_states = self._highlight_states
|
||||||
self:prepare_highlight_states(srow, erow)
|
self:prepare_highlight_states(srow, erow)
|
||||||
|
|
||||||
for row = srow, erow do
|
on_range_impl(self, buf, srow, 0, erow, 0, true)
|
||||||
on_line_impl(self, buf, row, true)
|
|
||||||
end
|
|
||||||
self._highlight_states = highlight_states
|
self._highlight_states = highlight_states
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -399,7 +435,7 @@ end
|
|||||||
|
|
||||||
api.nvim_set_decoration_provider(ns, {
|
api.nvim_set_decoration_provider(ns, {
|
||||||
on_win = TSHighlighter._on_win,
|
on_win = TSHighlighter._on_win,
|
||||||
on_line = TSHighlighter._on_line,
|
on_range = TSHighlighter._on_range,
|
||||||
_on_spell_nav = TSHighlighter._on_spell_nav,
|
_on_spell_nav = TSHighlighter._on_spell_nav,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
local api = vim.api
|
local api = vim.api
|
||||||
local language = require('vim.treesitter.language')
|
local language = require('vim.treesitter.language')
|
||||||
local memoize = vim.func._memoize
|
local memoize = vim.func._memoize
|
||||||
|
local cmp_ge = require('vim.treesitter._range').cmp_pos.ge
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@ -875,24 +876,33 @@ end
|
|||||||
---@param source (integer|string) Source buffer or string to extract text from
|
---@param source (integer|string) Source buffer or string to extract text from
|
||||||
---@param start? integer Starting line for the search. Defaults to `node:start()`.
|
---@param start? integer Starting line for the search. Defaults to `node:start()`.
|
||||||
---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`.
|
---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`.
|
||||||
|
---@param opts? { col_begin?: integer, col_end?: integer } Optional parameters.
|
||||||
---
|
---
|
||||||
---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch):
|
---@return (fun(end_line: integer|nil, end_col: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch):
|
||||||
--- capture id, capture node, metadata, match
|
--- capture id, capture node, metadata, match
|
||||||
---
|
---
|
||||||
---@note Captures are only returned if the query pattern of a specific capture contained predicates.
|
---@note Captures are only returned if the query pattern of a specific capture contained predicates.
|
||||||
function Query:iter_captures(node, source, start, stop)
|
function Query:iter_captures(node, source, start, stop, opts)
|
||||||
if type(source) == 'number' and source == 0 then
|
if type(source) == 'number' and source == 0 then
|
||||||
source = api.nvim_get_current_buf()
|
source = api.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
opts = opts or {}
|
||||||
start, stop = value_or_node_range(start, stop, node)
|
start, stop = value_or_node_range(start, stop, node)
|
||||||
|
|
||||||
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 })
|
local cursor_opts = {
|
||||||
|
row_begin = start,
|
||||||
|
col_begin = opts.col_begin,
|
||||||
|
row_end = stop,
|
||||||
|
col_end = opts.col_end,
|
||||||
|
match_limit = 256,
|
||||||
|
}
|
||||||
|
local cursor = vim._create_ts_querycursor(node, self.query, cursor_opts)
|
||||||
|
|
||||||
local apply_directives = memoize(match_id_hash, self.apply_directives, true)
|
local apply_directives = memoize(match_id_hash, self.apply_directives, true)
|
||||||
local match_preds = memoize(match_id_hash, self.match_preds, true)
|
local match_preds = memoize(match_id_hash, self.match_preds, true)
|
||||||
|
|
||||||
local function iter(end_line)
|
local function iter(end_line, end_col)
|
||||||
local capture, captured_node, match = cursor:next_capture()
|
local capture, captured_node, match = cursor:next_capture()
|
||||||
|
|
||||||
if not capture then
|
if not capture then
|
||||||
@ -902,10 +912,11 @@ function Query:iter_captures(node, source, start, stop)
|
|||||||
if not match_preds(self, match, source) then
|
if not match_preds(self, match, source) then
|
||||||
local match_id = match:info()
|
local match_id = match:info()
|
||||||
cursor:remove_match(match_id)
|
cursor:remove_match(match_id)
|
||||||
if end_line and captured_node:range() > end_line then
|
local row, col = captured_node:range()
|
||||||
|
if end_line and cmp_ge(row, col, end_line, end_col or 0) then
|
||||||
return nil, captured_node, nil, nil
|
return nil, captured_node, nil, nil
|
||||||
end
|
end
|
||||||
return iter(end_line) -- tail call: try next match
|
return iter(end_line, end_col) -- tail call: try next match
|
||||||
end
|
end
|
||||||
|
|
||||||
local metadata = apply_directives(self, match, source)
|
local metadata = apply_directives(self, match, source)
|
||||||
@ -961,8 +972,12 @@ function Query:iter_matches(node, source, start, stop, opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
start, stop = value_or_node_range(start, stop, node)
|
start, stop = value_or_node_range(start, stop, node)
|
||||||
|
opts.row_begin = start
|
||||||
|
opts.col_begin = 0
|
||||||
|
opts.row_end = stop
|
||||||
|
opts.col_end = 0
|
||||||
|
|
||||||
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
|
local cursor = vim._create_ts_querycursor(node, self.query, opts)
|
||||||
|
|
||||||
local function iter()
|
local function iter()
|
||||||
local match = cursor:next_match()
|
local match = cursor:next_match()
|
||||||
|
@ -992,8 +992,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
|||||||
/// Note: this function should not be called often. Rather, the callbacks
|
/// Note: this function should not be called often. Rather, the callbacks
|
||||||
/// themselves can be used to throttle unneeded callbacks. the `on_start`
|
/// themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||||
/// callback can return `false` to disable the provider until the next redraw.
|
/// callback can return `false` to disable the provider until the next redraw.
|
||||||
/// Similarly, return `false` in `on_win` will skip the `on_line` calls
|
/// Similarly, return `false` in `on_win` will skip the `on_line` and `on_range`
|
||||||
/// for that window (but any extmarks set in `on_win` will still be used).
|
/// calls for that window (but any extmarks set in `on_win` will still be used).
|
||||||
/// A plugin managing multiple sources of decoration should ideally only set
|
/// A plugin managing multiple sources of decoration should ideally only set
|
||||||
/// one provider, and merge the sources internally. You can use multiple `ns_id`
|
/// one provider, and merge the sources internally. You can use multiple `ns_id`
|
||||||
/// for the extmarks set/modified inside the callback anyway.
|
/// for the extmarks set/modified inside the callback anyway.
|
||||||
@ -1004,7 +1004,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
|||||||
/// Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
|
/// Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
|
||||||
/// for the moment.
|
/// for the moment.
|
||||||
///
|
///
|
||||||
/// Note: It is not allowed to remove or update extmarks in `on_line` callbacks.
|
/// Note: It is not allowed to remove or update extmarks
|
||||||
|
/// in `on_line` and `on_range` callbacks.
|
||||||
///
|
///
|
||||||
/// @param ns_id Namespace id from |nvim_create_namespace()|
|
/// @param ns_id Namespace id from |nvim_create_namespace()|
|
||||||
/// @param opts Table of callbacks:
|
/// @param opts Table of callbacks:
|
||||||
@ -1026,6 +1027,14 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
|||||||
/// ```
|
/// ```
|
||||||
/// ["line", winid, bufnr, row]
|
/// ["line", winid, bufnr, row]
|
||||||
/// ```
|
/// ```
|
||||||
|
/// - on_range: called for each buffer range being redrawn.
|
||||||
|
/// Range is end-exclusive and may span multiple lines. Range
|
||||||
|
/// bounds point to the first byte of a character. An end position
|
||||||
|
/// of the form (lnum, 0), including (number of lines, 0), is valid
|
||||||
|
/// and indicates that EOL of the preceding line is included.
|
||||||
|
/// ```
|
||||||
|
/// ["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
|
||||||
|
/// ```
|
||||||
/// - on_end: called at the end of a redraw cycle
|
/// - on_end: called at the end of a redraw cycle
|
||||||
/// ```
|
/// ```
|
||||||
/// ["end", tick]
|
/// ["end", tick]
|
||||||
@ -1049,6 +1058,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
|
|||||||
{ "on_buf", &opts->on_buf, &p->redraw_buf },
|
{ "on_buf", &opts->on_buf, &p->redraw_buf },
|
||||||
{ "on_win", &opts->on_win, &p->redraw_win },
|
{ "on_win", &opts->on_win, &p->redraw_win },
|
||||||
{ "on_line", &opts->on_line, &p->redraw_line },
|
{ "on_line", &opts->on_line, &p->redraw_line },
|
||||||
|
{ "on_range", &opts->on_range, &p->redraw_range },
|
||||||
{ "on_end", &opts->on_end, &p->redraw_end },
|
{ "on_end", &opts->on_end, &p->redraw_end },
|
||||||
{ "_on_hl_def", &opts->_on_hl_def, &p->hl_def },
|
{ "_on_hl_def", &opts->_on_hl_def, &p->hl_def },
|
||||||
{ "_on_spell_nav", &opts->_on_spell_nav, &p->spell_nav },
|
{ "_on_spell_nav", &opts->_on_spell_nav, &p->spell_nav },
|
||||||
|
@ -17,6 +17,8 @@ typedef struct {
|
|||||||
LuaRefOf(("buf" _, Integer bufnr, Integer tick)) on_buf;
|
LuaRefOf(("buf" _, Integer bufnr, Integer tick)) on_buf;
|
||||||
LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow)) on_win;
|
LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow)) on_win;
|
||||||
LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row)) on_line;
|
LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row)) on_line;
|
||||||
|
LuaRefOf(("range" _, Integer winid, Integer bufnr, Integer row_begin, Integer col_begin,
|
||||||
|
Integer row_end, Integer col_end)) on_range;
|
||||||
LuaRefOf(("end" _, Integer tick)) on_end;
|
LuaRefOf(("end" _, Integer tick)) on_end;
|
||||||
LuaRefOf(("hl_def" _)) _on_hl_def;
|
LuaRefOf(("hl_def" _)) _on_hl_def;
|
||||||
LuaRefOf(("spell_nav" _)) _on_spell_nav;
|
LuaRefOf(("spell_nav" _)) _on_spell_nav;
|
||||||
|
@ -140,6 +140,7 @@ typedef struct {
|
|||||||
LuaRef redraw_buf;
|
LuaRef redraw_buf;
|
||||||
LuaRef redraw_win;
|
LuaRef redraw_win;
|
||||||
LuaRef redraw_line;
|
LuaRef redraw_line;
|
||||||
|
LuaRef redraw_range;
|
||||||
LuaRef redraw_end;
|
LuaRef redraw_end;
|
||||||
LuaRef hl_def;
|
LuaRef hl_def;
|
||||||
LuaRef spell_nav;
|
LuaRef spell_nav;
|
||||||
|
@ -29,7 +29,7 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
|
|||||||
|
|
||||||
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
|
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
|
||||||
{ ns_id, kDecorProviderDisabled, LUA_NOREF, LUA_NOREF, \
|
{ ns_id, kDecorProviderDisabled, LUA_NOREF, LUA_NOREF, \
|
||||||
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
|
LUA_NOREF, LUA_NOREF, LUA_NOREF, LUA_NOREF, \
|
||||||
LUA_NOREF, -1, false, false, 0 }
|
LUA_NOREF, -1, false, false, 0 }
|
||||||
|
|
||||||
static void decor_provider_error(DecorProvider *provider, const char *name, const char *msg)
|
static void decor_provider_error(DecorProvider *provider, const char *name, const char *msg)
|
||||||
@ -178,6 +178,32 @@ void decor_providers_invoke_line(win_T *wp, int row, bool *has_decor)
|
|||||||
decor_state.running_decor_provider = false;
|
decor_state.running_decor_provider = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void decor_providers_invoke_range(win_T *wp, int sr, int sc, int er, int ec, bool *has_decor)
|
||||||
|
{
|
||||||
|
decor_state.running_decor_provider = true;
|
||||||
|
for (size_t i = 0; i < kv_size(decor_providers); i++) {
|
||||||
|
DecorProvider *p = &kv_A(decor_providers, i);
|
||||||
|
if (p->state == kDecorProviderActive && p->redraw_range != LUA_NOREF) {
|
||||||
|
MAXSIZE_TEMP_ARRAY(args, 6);
|
||||||
|
ADD_C(args, WINDOW_OBJ(wp->handle));
|
||||||
|
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
|
||||||
|
ADD_C(args, INTEGER_OBJ(sr));
|
||||||
|
ADD_C(args, INTEGER_OBJ(sc));
|
||||||
|
ADD_C(args, INTEGER_OBJ(er));
|
||||||
|
ADD_C(args, INTEGER_OBJ(ec));
|
||||||
|
if (decor_provider_invoke((int)i, "range", p->redraw_range, args, true)) {
|
||||||
|
*has_decor = true;
|
||||||
|
} else {
|
||||||
|
// return 'false' or error: skip rest of this window
|
||||||
|
kv_A(decor_providers, i).state = kDecorProviderWinDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
hl_check_ns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decor_state.running_decor_provider = false;
|
||||||
|
}
|
||||||
|
|
||||||
/// For each provider invoke the 'buf' callback for a given buffer.
|
/// For each provider invoke the 'buf' callback for a given buffer.
|
||||||
///
|
///
|
||||||
/// @param buf Buffer
|
/// @param buf Buffer
|
||||||
@ -262,6 +288,7 @@ void decor_provider_clear(DecorProvider *p)
|
|||||||
NLUA_CLEAR_REF(p->redraw_buf);
|
NLUA_CLEAR_REF(p->redraw_buf);
|
||||||
NLUA_CLEAR_REF(p->redraw_win);
|
NLUA_CLEAR_REF(p->redraw_win);
|
||||||
NLUA_CLEAR_REF(p->redraw_line);
|
NLUA_CLEAR_REF(p->redraw_line);
|
||||||
|
NLUA_CLEAR_REF(p->redraw_range);
|
||||||
NLUA_CLEAR_REF(p->redraw_end);
|
NLUA_CLEAR_REF(p->redraw_end);
|
||||||
NLUA_CLEAR_REF(p->spell_nav);
|
NLUA_CLEAR_REF(p->spell_nav);
|
||||||
p->state = kDecorProviderDisabled;
|
p->state = kDecorProviderDisabled;
|
||||||
|
@ -955,7 +955,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
bool area_highlighting = false; // Visual or incsearch highlighting in this line
|
bool area_highlighting = false; // Visual or incsearch highlighting in this line
|
||||||
int vi_attr = 0; // attributes for Visual and incsearch highlighting
|
int vi_attr = 0; // attributes for Visual and incsearch highlighting
|
||||||
int area_attr = 0; // attributes desired by highlighting
|
int area_attr = 0; // attributes desired by highlighting
|
||||||
int search_attr = 0; // attributes desired by 'hlsearch'
|
int search_attr = 0; // attributes desired by 'hlsearch' or ComplMatchIns
|
||||||
int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
|
int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
|
||||||
int decor_attr = 0; // attributes desired by syntax and extmarks
|
int decor_attr = 0; // attributes desired by syntax and extmarks
|
||||||
bool has_syntax = false; // this buffer has syntax highl.
|
bool has_syntax = false; // this buffer has syntax highl.
|
||||||
@ -969,7 +969,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
int spell_attr = 0; // attributes desired by spelling
|
int spell_attr = 0; // attributes desired by spelling
|
||||||
int word_end = 0; // last byte with same spell_attr
|
int word_end = 0; // last byte with same spell_attr
|
||||||
int cur_checked_col = 0; // checked column for current line
|
int cur_checked_col = 0; // checked column for current line
|
||||||
bool extra_check = 0; // has syntax or linebreak
|
bool extra_check = false; // has extra highlighting
|
||||||
int multi_attr = 0; // attributes desired by multibyte
|
int multi_attr = 0; // attributes desired by multibyte
|
||||||
int mb_l = 1; // multi-byte byte length
|
int mb_l = 1; // multi-byte byte length
|
||||||
int mb_c = 0; // decoded multi-byte character
|
int mb_c = 0; // decoded multi-byte character
|
||||||
@ -1029,6 +1029,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
buf_T *buf = wp->w_buffer;
|
buf_T *buf = wp->w_buffer;
|
||||||
const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
|
const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
|
||||||
|
|
||||||
|
int decor_provider_end_col;
|
||||||
|
bool check_decor_providers = false;
|
||||||
|
|
||||||
if (col_rows == 0) {
|
if (col_rows == 0) {
|
||||||
// To speed up the loop below, set extra_check when there is linebreak,
|
// To speed up the loop below, set extra_check when there is linebreak,
|
||||||
// trailing white space and/or syntax processing to be done.
|
// trailing white space and/or syntax processing to be done.
|
||||||
@ -1054,11 +1057,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
|
has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
|
||||||
|
|
||||||
if (!end_fill) {
|
if (!end_fill) {
|
||||||
decor_providers_invoke_line(wp, lnum - 1, &has_decor);
|
check_decor_providers = true;
|
||||||
}
|
|
||||||
|
|
||||||
if (has_decor) {
|
|
||||||
extra_check = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for columns to display for 'colorcolumn'.
|
// Check for columns to display for 'colorcolumn'.
|
||||||
@ -1468,6 +1467,35 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (check_decor_providers) {
|
||||||
|
int const col = (int)(ptr - line);
|
||||||
|
|
||||||
|
// Approximate the number of bytes that will be drawn.
|
||||||
|
// Assume we're dealing with 1-cell ascii and ignore
|
||||||
|
// the effects of 'linebreak', 'breakindent', etc.
|
||||||
|
int rem_vcols;
|
||||||
|
if (wp->w_p_wrap) {
|
||||||
|
int width = wp->w_width_inner - win_col_off(wp);
|
||||||
|
int width2 = width + win_col_off2(wp);
|
||||||
|
|
||||||
|
int first_row_width = start_col == 0 ? width : width2;
|
||||||
|
rem_vcols = first_row_width + (endrow - startrow - 1) * width2;
|
||||||
|
} else {
|
||||||
|
rem_vcols = wp->w_width_inner - win_col_off(wp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call it here since we need to invalidate the line pointer anyway.
|
||||||
|
decor_providers_invoke_line(wp, lnum - 1, &has_decor);
|
||||||
|
|
||||||
|
decor_provider_end_col = invoke_range_next(wp, lnum, col, rem_vcols + 1, &has_decor);
|
||||||
|
|
||||||
|
line = ml_get_buf(wp->w_buffer, lnum);
|
||||||
|
ptr = line + col;
|
||||||
|
}
|
||||||
|
if (has_decor) {
|
||||||
|
extra_check = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Correct highlighting for cursor that can't be disabled.
|
// Correct highlighting for cursor that can't be disabled.
|
||||||
// Avoids having to check this for each character.
|
// Avoids having to check this for each character.
|
||||||
if (wlv.fromcol >= 0) {
|
if (wlv.fromcol >= 0) {
|
||||||
@ -1495,6 +1523,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
ptr = line + v; // "line" may have been updated
|
ptr = line + v; // "line" may have been updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((State & MODE_INSERT) && in_curline && ins_compl_active()) {
|
||||||
|
area_highlighting = true;
|
||||||
|
}
|
||||||
|
|
||||||
win_line_start(wp, &wlv);
|
win_line_start(wp, &wlv);
|
||||||
bool draw_cols = true;
|
bool draw_cols = true;
|
||||||
int leftcols_width = 0;
|
int leftcols_width = 0;
|
||||||
@ -1517,6 +1549,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
|
|
||||||
bool did_decrement_ptr = false;
|
bool did_decrement_ptr = false;
|
||||||
|
|
||||||
|
if (check_decor_providers && (int)(ptr - line) >= decor_provider_end_col) {
|
||||||
|
int const col = (int)(ptr - line);
|
||||||
|
decor_provider_end_col = invoke_range_next(wp, lnum, col, 100, &has_decor);
|
||||||
|
line = ml_get_buf(wp->w_buffer, lnum);
|
||||||
|
ptr = line + col;
|
||||||
|
}
|
||||||
|
if (has_decor) {
|
||||||
|
extra_check = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Skip this quickly when working on the text.
|
// Skip this quickly when working on the text.
|
||||||
if (draw_cols) {
|
if (draw_cols) {
|
||||||
if (cul_screenline) {
|
if (cul_screenline) {
|
||||||
@ -1740,6 +1782,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
if (*ptr == NUL) {
|
if (*ptr == NUL) {
|
||||||
has_match_conc = 0;
|
has_match_conc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if ComplMatchIns highlight is needed.
|
||||||
|
if ((State & MODE_INSERT) && in_curline && ins_compl_active()) {
|
||||||
|
int ins_match_attr = ins_compl_col_range_attr((int)(ptr - line));
|
||||||
|
if (ins_match_attr > 0) {
|
||||||
|
search_attr = hl_combine_attr(search_attr, ins_match_attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wlv.diff_hlf != (hlf_T)0) {
|
if (wlv.diff_hlf != (hlf_T)0) {
|
||||||
@ -1787,16 +1837,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
|||||||
wlv.char_attr = hl_combine_attr(char_attr_base, char_attr_pri);
|
wlv.char_attr = hl_combine_attr(char_attr_base, char_attr_pri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply ComplMatchIns highlight if needed.
|
|
||||||
if (wlv.filler_todo <= 0
|
|
||||||
&& (State & MODE_INSERT) && in_curline && ins_compl_active()) {
|
|
||||||
int ins_match_attr = ins_compl_col_range_attr((int)(ptr - line));
|
|
||||||
if (ins_match_attr > 0) {
|
|
||||||
char_attr_pri = hl_combine_attr(char_attr_pri, ins_match_attr);
|
|
||||||
wlv.char_attr = hl_combine_attr(char_attr_base, char_attr_pri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (draw_folded && has_foldtext && wlv.n_extra == 0 && wlv.col == win_col_offset) {
|
if (draw_folded && has_foldtext && wlv.n_extra == 0 && wlv.col == win_col_offset) {
|
||||||
const int v = (int)(ptr - line);
|
const int v = (int)(ptr - line);
|
||||||
linenr_T lnume = lnum + foldinfo.fi_lines - 1;
|
linenr_T lnume = lnum + foldinfo.fi_lines - 1;
|
||||||
@ -2999,3 +3039,25 @@ static void wlv_put_linebuf(win_T *wp, const winlinevars_T *wlv, int endcol, boo
|
|||||||
grid_adjust(&grid, &row, &coloff);
|
grid_adjust(&grid, &row, &coloff);
|
||||||
grid_put_linebuf(grid, row, coloff, startcol, endcol, clear_width, bg_attr, wlv->vcol - 1, flags);
|
grid_put_linebuf(grid, row, coloff, startcol, endcol, clear_width, bg_attr, wlv->vcol - 1, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return New begin column, or INT_MAX.
|
||||||
|
static int invoke_range_next(win_T *wp, int lnum, colnr_T begin_col, colnr_T col_off,
|
||||||
|
bool *has_decor)
|
||||||
|
{
|
||||||
|
char const *const line = ml_get_buf(wp->w_buffer, lnum);
|
||||||
|
int const line_len = ml_get_buf_len(wp->w_buffer, lnum);
|
||||||
|
col_off = MAX(col_off, 1);
|
||||||
|
|
||||||
|
colnr_T new_col;
|
||||||
|
if (col_off <= line_len - begin_col) {
|
||||||
|
int end_col = begin_col + col_off;
|
||||||
|
end_col += mb_off_next(line, line + end_col);
|
||||||
|
decor_providers_invoke_range(wp, lnum - 1, begin_col, lnum - 1, end_col, has_decor);
|
||||||
|
new_col = end_col;
|
||||||
|
} else {
|
||||||
|
decor_providers_invoke_range(wp, lnum - 1, begin_col, lnum, 0, has_decor);
|
||||||
|
new_col = INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_col;
|
||||||
|
}
|
||||||
|
@ -173,7 +173,7 @@ static const char *highlight_init_both[] = {
|
|||||||
"default link PmenuKind Pmenu",
|
"default link PmenuKind Pmenu",
|
||||||
"default link PmenuKindSel PmenuSel",
|
"default link PmenuKindSel PmenuSel",
|
||||||
"default link PmenuSbar Pmenu",
|
"default link PmenuSbar Pmenu",
|
||||||
"default link ComplMatchIns Normal",
|
"default link ComplMatchIns NONE",
|
||||||
"default link Substitute Search",
|
"default link Substitute Search",
|
||||||
"default link StatusLineTerm StatusLine",
|
"default link StatusLineTerm StatusLine",
|
||||||
"default link StatusLineTermNC StatusLineNC",
|
"default link StatusLineTermNC StatusLineNC",
|
||||||
|
@ -958,7 +958,7 @@ static void ins_compl_insert_bytes(char *p, int len)
|
|||||||
}
|
}
|
||||||
assert(len >= 0);
|
assert(len >= 0);
|
||||||
ins_bytes_len(p, (size_t)len);
|
ins_bytes_len(p, (size_t)len);
|
||||||
compl_ins_end_col = curwin->w_cursor.col - 1;
|
compl_ins_end_col = curwin->w_cursor.col;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the column is within the currently inserted completion text
|
/// Checks if the column is within the currently inserted completion text
|
||||||
@ -2147,6 +2147,8 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
|
|||||||
&& pum_visible()) {
|
&& pum_visible()) {
|
||||||
word = xstrdup(compl_shown_match->cp_str);
|
word = xstrdup(compl_shown_match->cp_str);
|
||||||
retval = true;
|
retval = true;
|
||||||
|
// May need to remove ComplMatchIns highlight.
|
||||||
|
redrawWinline(curwin, curwin->w_cursor.lnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CTRL-E means completion is Ended, go back to the typed text.
|
// CTRL-E means completion is Ended, go back to the typed text.
|
||||||
@ -3648,6 +3650,7 @@ void ins_compl_delete(bool new_leader)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
backspace_until_column(col);
|
backspace_until_column(col);
|
||||||
|
compl_ins_end_col = curwin->w_cursor.col;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(vim): is this sufficient for redrawing? Redrawing everything
|
// TODO(vim): is this sufficient for redrawing? Redrawing everything
|
||||||
|
@ -1287,40 +1287,68 @@ static struct luaL_Reg querycursor_meta[] = {
|
|||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline bool set_uint32(lua_State *L, int idx, char const *name, uint32_t *res)
|
||||||
|
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
lua_getfield(L, idx, name);
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t value = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
if (value < 0U) {
|
||||||
|
*res = 0U;
|
||||||
|
} else if (value > UINT32_MAX) {
|
||||||
|
*res = UINT32_MAX;
|
||||||
|
} else {
|
||||||
|
*res = (uint32_t)value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int tslua_push_querycursor(lua_State *L)
|
int tslua_push_querycursor(lua_State *L)
|
||||||
{
|
{
|
||||||
TSNode node = node_check(L, 1);
|
TSNode node = node_check(L, 1);
|
||||||
|
|
||||||
TSQuery *query = query_check(L, 2);
|
TSQuery *query = query_check(L, 2);
|
||||||
TSQueryCursor *cursor = ts_query_cursor_new();
|
TSQueryCursor *cursor = ts_query_cursor_new();
|
||||||
ts_query_cursor_exec(cursor, query, node);
|
|
||||||
|
|
||||||
if (lua_gettop(L) >= 3) {
|
if (lua_gettop(L) >= 3 && !lua_isnil(L, 3)) {
|
||||||
uint32_t start = (uint32_t)luaL_checkinteger(L, 3);
|
luaL_argcheck(L, lua_istable(L, 3), 3, "table expected");
|
||||||
uint32_t end = lua_gettop(L) >= 4 ? (uint32_t)luaL_checkinteger(L, 4) : MAXLNUM;
|
|
||||||
ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lua_gettop(L) >= 5 && !lua_isnil(L, 5)) {
|
TSPoint begin = (TSPoint){ 0, 0 };
|
||||||
luaL_argcheck(L, lua_istable(L, 5), 5, "table expected");
|
TSPoint end = (TSPoint){ UINT32_MAX, UINT32_MAX };
|
||||||
lua_pushnil(L); // [dict, ..., nil]
|
|
||||||
while (lua_next(L, 5)) {
|
if (set_uint32(L, 3, "row_begin", &begin.row)) {
|
||||||
// [dict, ..., key, value]
|
if (!set_uint32(L, 3, "col_begin", &begin.column)) {
|
||||||
if (lua_type(L, -2) == LUA_TSTRING) {
|
begin.column = 0;
|
||||||
char *k = (char *)lua_tostring(L, -2);
|
|
||||||
if (strequal("max_start_depth", k)) {
|
|
||||||
uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1);
|
|
||||||
ts_query_cursor_set_max_start_depth(cursor, max_start_depth);
|
|
||||||
} else if (strequal("match_limit", k)) {
|
|
||||||
uint32_t match_limit = (uint32_t)lua_tointeger(L, -1);
|
|
||||||
ts_query_cursor_set_match_limit(cursor, match_limit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// pop the value; lua_next will pop the key.
|
}
|
||||||
lua_pop(L, 1); // [dict, ..., key]
|
|
||||||
|
if (set_uint32(L, 3, "row_end", &end.row)) {
|
||||||
|
if (!set_uint32(L, 3, "col_end", &end.column)) {
|
||||||
|
end.column = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ts_query_cursor_set_point_range(cursor, begin, end);
|
||||||
|
|
||||||
|
uint32_t max_start_depth;
|
||||||
|
if (set_uint32(L, 3, "max_start_depth", &max_start_depth)) {
|
||||||
|
ts_query_cursor_set_max_start_depth(cursor, max_start_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t match_limit;
|
||||||
|
if (set_uint32(L, 3, "match_limit", &match_limit)) {
|
||||||
|
ts_query_cursor_set_match_limit(cursor, match_limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ts_query_cursor_exec(cursor, query, node);
|
||||||
|
|
||||||
TSQueryCursor **ud = lua_newuserdata(L, sizeof(*ud)); // [node, query, ..., udata]
|
TSQueryCursor **ud = lua_newuserdata(L, sizeof(*ud)); // [node, query, ..., udata]
|
||||||
*ud = cursor;
|
*ud = cursor;
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYCURSOR); // [node, query, ..., udata, meta]
|
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYCURSOR); // [node, query, ..., udata, meta]
|
||||||
|
@ -641,9 +641,6 @@ bool terminal_enter(void)
|
|||||||
curwin->w_p_so = 0;
|
curwin->w_p_so = 0;
|
||||||
curwin->w_p_siso = 0;
|
curwin->w_p_siso = 0;
|
||||||
|
|
||||||
// Save the existing cursor entry since it may be modified by the application
|
|
||||||
cursorentry_T save_cursorentry = shape_table[SHAPE_IDX_TERM];
|
|
||||||
|
|
||||||
// Update the cursor shape table and flush changes to the UI
|
// Update the cursor shape table and flush changes to the UI
|
||||||
s->term->pending.cursor = true;
|
s->term->pending.cursor = true;
|
||||||
refresh_cursor(s->term);
|
refresh_cursor(s->term);
|
||||||
@ -674,8 +671,8 @@ bool terminal_enter(void)
|
|||||||
RedrawingDisabled = s->save_rd;
|
RedrawingDisabled = s->save_rd;
|
||||||
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
|
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
|
||||||
|
|
||||||
shape_table[SHAPE_IDX_TERM] = save_cursorentry;
|
// Restore the terminal cursor to what is set in 'guicursor'
|
||||||
ui_mode_info_set();
|
(void)parse_shape_opt(SHAPE_CURSOR);
|
||||||
|
|
||||||
if (save_curwin == curwin->handle) { // Else: window was closed.
|
if (save_curwin == curwin->handle) { // Else: window was closed.
|
||||||
curwin->w_p_cul = save_w_p_cul;
|
curwin->w_p_cul = save_w_p_cul;
|
||||||
|
@ -6,10 +6,9 @@ describe('decor perf', function()
|
|||||||
before_each(n.clear)
|
before_each(n.clear)
|
||||||
|
|
||||||
it('can handle long lines', function()
|
it('can handle long lines', function()
|
||||||
local screen = Screen.new(100, 101)
|
Screen.new(100, 101)
|
||||||
screen:attach()
|
|
||||||
|
|
||||||
local result = exec_lua [==[
|
local result = exec_lua(function()
|
||||||
local ephemeral_pattern = {
|
local ephemeral_pattern = {
|
||||||
{ 0, 4, 'Comment', 11 },
|
{ 0, 4, 'Comment', 11 },
|
||||||
{ 0, 3, 'Keyword', 12 },
|
{ 0, 3, 'Keyword', 12 },
|
||||||
@ -62,7 +61,7 @@ describe('decor perf', function()
|
|||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
on_line = function()
|
on_line = function()
|
||||||
add_pattern(ephemeral_pattern, true)
|
add_pattern(ephemeral_pattern, true)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -70,16 +69,16 @@ describe('decor perf', function()
|
|||||||
|
|
||||||
local total = {}
|
local total = {}
|
||||||
local provider = {}
|
local provider = {}
|
||||||
for i = 1, 100 do
|
for _ = 1, 100 do
|
||||||
local tic = vim.uv.hrtime()
|
local tic = vim.uv.hrtime()
|
||||||
vim.cmd'redraw!'
|
vim.cmd 'redraw!'
|
||||||
local toc = vim.uv.hrtime()
|
local toc = vim.uv.hrtime()
|
||||||
table.insert(total, toc - tic)
|
table.insert(total, toc - tic)
|
||||||
table.insert(provider, pe - ps)
|
table.insert(provider, pe - ps)
|
||||||
end
|
end
|
||||||
|
|
||||||
return { total, provider }
|
return { total, provider }
|
||||||
]==]
|
end)
|
||||||
|
|
||||||
local total, provider = unpack(result)
|
local total, provider = unpack(result)
|
||||||
table.sort(total)
|
table.sort(total)
|
||||||
@ -99,4 +98,39 @@ describe('decor perf', function()
|
|||||||
|
|
||||||
print('\nTotal ' .. fmt(total) .. '\nDecoration provider: ' .. fmt(provider))
|
print('\nTotal ' .. fmt(total) .. '\nDecoration provider: ' .. fmt(provider))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('can handle long lines with treesitter highlighting', function()
|
||||||
|
Screen.new(100, 51)
|
||||||
|
|
||||||
|
local result = exec_lua(function()
|
||||||
|
local long_line = 'local a = { ' .. ('a = 5, '):rep(2000) .. '}'
|
||||||
|
vim.api.nvim_buf_set_lines(0, 0, 0, false, { long_line })
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 1, 0 })
|
||||||
|
vim.treesitter.start(0, 'lua')
|
||||||
|
|
||||||
|
local total = {}
|
||||||
|
for _ = 1, 50 do
|
||||||
|
local tic = vim.uv.hrtime()
|
||||||
|
vim.cmd 'redraw!'
|
||||||
|
local toc = vim.uv.hrtime()
|
||||||
|
table.insert(total, toc - tic)
|
||||||
|
end
|
||||||
|
|
||||||
|
return { total }
|
||||||
|
end)
|
||||||
|
|
||||||
|
local total = unpack(result)
|
||||||
|
table.sort(total)
|
||||||
|
|
||||||
|
local ms = 1 / 1000000
|
||||||
|
local res = string.format(
|
||||||
|
'min, 25%%, median, 75%%, max:\n\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms',
|
||||||
|
total[1] * ms,
|
||||||
|
total[1 + math.floor(#total * 0.25)] * ms,
|
||||||
|
total[1 + math.floor(#total * 0.5)] * ms,
|
||||||
|
total[1 + math.floor(#total * 0.75)] * ms,
|
||||||
|
total[#total] * ms
|
||||||
|
)
|
||||||
|
print('\nTotal ' .. res)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
local n = require('test.functional.testnvim')()
|
local n = require('test.functional.testnvim')()
|
||||||
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
|
||||||
local clear = n.clear
|
local clear = n.clear
|
||||||
local exec_lua = n.exec_lua
|
local exec_lua = n.exec_lua
|
||||||
|
|
||||||
describe('treesitter perf', function()
|
describe('treesitter perf', function()
|
||||||
setup(function()
|
before_each(function()
|
||||||
clear()
|
clear()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -47,4 +48,144 @@ describe('treesitter perf', function()
|
|||||||
return vim.uv.hrtime() - start
|
return vim.uv.hrtime() - start
|
||||||
]]
|
]]
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local function test_long_line(_pos, _wrap, _line, grid)
|
||||||
|
local screen = Screen.new(20, 11)
|
||||||
|
|
||||||
|
local result = exec_lua(function(...)
|
||||||
|
local pos, wrap, line = ...
|
||||||
|
|
||||||
|
vim.api.nvim_buf_set_lines(0, 0, 0, false, { line })
|
||||||
|
vim.api.nvim_win_set_cursor(0, pos)
|
||||||
|
vim.api.nvim_set_option_value('wrap', wrap, { win = 0 })
|
||||||
|
|
||||||
|
vim.treesitter.start(0, 'lua')
|
||||||
|
|
||||||
|
local total = {}
|
||||||
|
for _ = 1, 100 do
|
||||||
|
local tic = vim.uv.hrtime()
|
||||||
|
vim.cmd 'redraw!'
|
||||||
|
local toc = vim.uv.hrtime()
|
||||||
|
table.insert(total, toc - tic)
|
||||||
|
end
|
||||||
|
|
||||||
|
return { total }
|
||||||
|
end, _pos, _wrap, _line)
|
||||||
|
|
||||||
|
screen:expect({ grid = grid or '' })
|
||||||
|
|
||||||
|
local total = unpack(result)
|
||||||
|
table.sort(total)
|
||||||
|
|
||||||
|
local ms = 1 / 1000000
|
||||||
|
local res = string.format(
|
||||||
|
'min, 25%%, median, 75%%, max:\n\t%0.2fms,\t%0.2fms,\t%0.2fms,\t%0.2fms,\t%0.2fms',
|
||||||
|
total[1] * ms,
|
||||||
|
total[1 + math.floor(#total * 0.25)] * ms,
|
||||||
|
total[1 + math.floor(#total * 0.5)] * ms,
|
||||||
|
total[1 + math.floor(#total * 0.75)] * ms,
|
||||||
|
total[#total] * ms
|
||||||
|
)
|
||||||
|
print('\nTotal ' .. res)
|
||||||
|
end
|
||||||
|
|
||||||
|
local long_line = 'local a = { ' .. ('a = 5, '):rep(500) .. '}'
|
||||||
|
it('can redraw the beginning of a long line with wrapping', function()
|
||||||
|
local grid = [[
|
||||||
|
{15:^local} {25:a} {15:=} {16:{} {25:a} {15:=} {26:5}{16:,} {25:a}|
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} |
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,}|
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}|
|
||||||
|
{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} |
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=}|
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} |
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a}|
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} |
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,}|
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
test_long_line({ 1, 0 }, true, long_line, grid)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can redraw the middle of a long line with wrapping', function()
|
||||||
|
local grid = [[
|
||||||
|
{1:<<<}{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} |
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,}|
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}|
|
||||||
|
{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} |
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=}|
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} |
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a}|
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} |
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,}|
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a}^ {15:=} {26:5}|
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
test_long_line({ 1, math.floor(#long_line / 2) }, true, long_line, grid)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can redraw the end of a long line with wrapping', function()
|
||||||
|
local grid = [[
|
||||||
|
{1:<<<}{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=}|
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} |
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a}|
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} |
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,}|
|
||||||
|
{25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}|
|
||||||
|
{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} |
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=}|
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {25:a} |
|
||||||
|
{15:=} {26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {16:^}} |
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
test_long_line({ 1, #long_line - 1 }, true, long_line, grid)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can redraw the beginning of a long line without wrapping', function()
|
||||||
|
local grid = [[
|
||||||
|
{15:^local} {25:a} {15:=} {16:{} {25:a} {15:=} {26:5}{16:,} {25:a}|
|
||||||
|
|
|
||||||
|
{1:~ }|*8
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
test_long_line({ 1, 0 }, false, long_line, grid)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can redraw the middle of a long line without wrapping', function()
|
||||||
|
local grid = [[
|
||||||
|
{16:,} {25:a} {15:=} {26:5}{16:,} {25:a}^ {15:=} {26:5}{16:,} {25:a} {15:=} |
|
||||||
|
|
|
||||||
|
{1:~ }|*8
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
test_long_line({ 1, math.floor(#long_line / 2) }, false, long_line, grid)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can redraw the end of a long line without wrapping', function()
|
||||||
|
local grid = [[
|
||||||
|
{26:5}{16:,} {25:a} {15:=} {26:5}{16:,} {16:^}} |
|
||||||
|
|
|
||||||
|
{1:~ }|*8
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
test_long_line({ 1, #long_line - 1 }, false, long_line, grid)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local long_line_mb = 'local a = { ' .. ('À = 5, '):rep(500) .. '}'
|
||||||
|
it('can redraw the middle of a long line with multibyte characters', function()
|
||||||
|
local grid = [[
|
||||||
|
{1:<<<}{26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} |
|
||||||
|
{25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,}|
|
||||||
|
{25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}|
|
||||||
|
{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} |
|
||||||
|
{26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=}|
|
||||||
|
{26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} |
|
||||||
|
{15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À}|
|
||||||
|
{15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} |
|
||||||
|
{25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,}|
|
||||||
|
{25:À} {15:=} {26:5}{16:,} {25:À} {15:=} {26:5}{16:,} {25:À}^ {15:=} {26:5}|
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
test_long_line({ 1, math.floor(#long_line_mb / 2) }, true, long_line_mb, grid)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -15,9 +15,20 @@ local skip = t.skip
|
|||||||
describe(':terminal cursor', function()
|
describe(':terminal cursor', function()
|
||||||
local screen
|
local screen
|
||||||
|
|
||||||
|
local terminal_mode_idx ---@type number
|
||||||
|
|
||||||
before_each(function()
|
before_each(function()
|
||||||
clear()
|
clear()
|
||||||
screen = tt.setup_screen()
|
screen = tt.setup_screen()
|
||||||
|
|
||||||
|
if terminal_mode_idx == nil then
|
||||||
|
for i, v in ipairs(screen._mode_info) do
|
||||||
|
if v.name == 'terminal' then
|
||||||
|
terminal_mode_idx = i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert(terminal_mode_idx)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('moves the screen cursor when focused', function()
|
it('moves the screen cursor when focused', function()
|
||||||
@ -143,13 +154,6 @@ describe(':terminal cursor', function()
|
|||||||
|
|
||||||
it('can be modified by application #3681', function()
|
it('can be modified by application #3681', function()
|
||||||
skip(is_os('win'), '#31587')
|
skip(is_os('win'), '#31587')
|
||||||
local idx ---@type number
|
|
||||||
for i, v in ipairs(screen._mode_info) do
|
|
||||||
if v.name == 'terminal' then
|
|
||||||
idx = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert(idx)
|
|
||||||
|
|
||||||
local states = {
|
local states = {
|
||||||
[1] = { blink = true, shape = 'block' },
|
[1] = { blink = true, shape = 'block' },
|
||||||
@ -171,13 +175,13 @@ describe(':terminal cursor', function()
|
|||||||
]],
|
]],
|
||||||
condition = function()
|
condition = function()
|
||||||
if v.blink then
|
if v.blink then
|
||||||
eq(500, screen._mode_info[idx].blinkon)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(500, screen._mode_info[idx].blinkoff)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
else
|
else
|
||||||
eq(0, screen._mode_info[idx].blinkon)
|
eq(0, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(0, screen._mode_info[idx].blinkoff)
|
eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
end
|
end
|
||||||
eq(v.shape, screen._mode_info[idx].cursor_shape)
|
eq(v.shape, screen._mode_info[terminal_mode_idx].cursor_shape)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
@ -191,20 +195,13 @@ describe(':terminal cursor', function()
|
|||||||
]])
|
]])
|
||||||
|
|
||||||
-- Cursor returns to default on TermLeave
|
-- Cursor returns to default on TermLeave
|
||||||
eq(500, screen._mode_info[idx].blinkon)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(500, screen._mode_info[idx].blinkoff)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
eq('block', screen._mode_info[idx].cursor_shape)
|
eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can be modified per terminal', function()
|
it('can be modified per terminal', function()
|
||||||
skip(is_os('win'), '#31587')
|
skip(is_os('win'), '#31587')
|
||||||
local idx ---@type number
|
|
||||||
for i, v in ipairs(screen._mode_info) do
|
|
||||||
if v.name == 'terminal' then
|
|
||||||
idx = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert(idx)
|
|
||||||
|
|
||||||
-- Set cursor to vertical bar with blink
|
-- Set cursor to vertical bar with blink
|
||||||
tt.feed_csi('5 q')
|
tt.feed_csi('5 q')
|
||||||
@ -216,9 +213,9 @@ describe(':terminal cursor', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]],
|
]],
|
||||||
condition = function()
|
condition = function()
|
||||||
eq(500, screen._mode_info[idx].blinkon)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(500, screen._mode_info[idx].blinkoff)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
eq('vertical', screen._mode_info[idx].cursor_shape)
|
eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -231,9 +228,9 @@ describe(':terminal cursor', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]],
|
]],
|
||||||
condition = function()
|
condition = function()
|
||||||
eq(500, screen._mode_info[idx].blinkon)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(500, screen._mode_info[idx].blinkoff)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
eq('vertical', screen._mode_info[idx].cursor_shape)
|
eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -256,9 +253,9 @@ describe(':terminal cursor', function()
|
|||||||
]],
|
]],
|
||||||
condition = function()
|
condition = function()
|
||||||
-- New terminal, cursor resets to defaults
|
-- New terminal, cursor resets to defaults
|
||||||
eq(500, screen._mode_info[idx].blinkon)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(500, screen._mode_info[idx].blinkoff)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
eq('block', screen._mode_info[idx].cursor_shape)
|
eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -275,9 +272,9 @@ describe(':terminal cursor', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]],
|
]],
|
||||||
condition = function()
|
condition = function()
|
||||||
eq(0, screen._mode_info[idx].blinkon)
|
eq(0, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(0, screen._mode_info[idx].blinkoff)
|
eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
eq('horizontal', screen._mode_info[idx].cursor_shape)
|
eq('horizontal', screen._mode_info[terminal_mode_idx].cursor_shape)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -294,9 +291,9 @@ describe(':terminal cursor', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]],
|
]],
|
||||||
condition = function()
|
condition = function()
|
||||||
eq(500, screen._mode_info[idx].blinkon)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
|
||||||
eq(500, screen._mode_info[idx].blinkoff)
|
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
|
||||||
eq('vertical', screen._mode_info[idx].cursor_shape)
|
eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
@ -326,6 +323,32 @@ describe(':terminal cursor', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('preserves guicursor value on TermLeave #31612', function()
|
||||||
|
eq(3, screen._mode_info[terminal_mode_idx].hl_id)
|
||||||
|
|
||||||
|
-- Change 'guicursor' while terminal mode is active
|
||||||
|
command('set guicursor+=t:Error')
|
||||||
|
|
||||||
|
local error_hl_id = call('hlID', 'Error')
|
||||||
|
|
||||||
|
screen:expect({
|
||||||
|
condition = function()
|
||||||
|
eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Exit terminal mode
|
||||||
|
feed([[<C-\><C-N>]])
|
||||||
|
|
||||||
|
screen:expect([[
|
||||||
|
tty ready |
|
||||||
|
^ |
|
||||||
|
|*5
|
||||||
|
]])
|
||||||
|
|
||||||
|
eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('buffer cursor position is correct in terminal without number column', function()
|
describe('buffer cursor position is correct in terminal without number column', function()
|
||||||
@ -350,12 +373,6 @@ describe('buffer cursor position is correct in terminal without number column',
|
|||||||
}, {
|
}, {
|
||||||
cols = 70,
|
cols = 70,
|
||||||
})
|
})
|
||||||
screen:set_default_attr_ids({
|
|
||||||
[1] = { foreground = 253, background = 11 },
|
|
||||||
[2] = { reverse = true },
|
|
||||||
[3] = { bold = true },
|
|
||||||
[4] = { background = 11 },
|
|
||||||
})
|
|
||||||
-- Also check for real cursor position, as it is used for stuff like input methods
|
-- Also check for real cursor position, as it is used for stuff like input methods
|
||||||
screen._handle_busy_start = function() end
|
screen._handle_busy_start = function() end
|
||||||
screen._handle_busy_stop = function() end
|
screen._handle_busy_stop = function() end
|
||||||
@ -667,13 +684,6 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
|||||||
}, {
|
}, {
|
||||||
cols = 70,
|
cols = 70,
|
||||||
})
|
})
|
||||||
screen:set_default_attr_ids({
|
|
||||||
[1] = { foreground = 253, background = 11 },
|
|
||||||
[2] = { reverse = true },
|
|
||||||
[3] = { bold = true },
|
|
||||||
[4] = { background = 11 },
|
|
||||||
[7] = { foreground = 130 },
|
|
||||||
})
|
|
||||||
-- Also check for real cursor position, as it is used for stuff like input methods
|
-- Also check for real cursor position, as it is used for stuff like input methods
|
||||||
screen._handle_busy_start = function() end
|
screen._handle_busy_start = function() end
|
||||||
screen._handle_busy_stop = function() end
|
screen._handle_busy_stop = function() end
|
||||||
|
@ -717,6 +717,47 @@ void ui_refresh(void)
|
|||||||
eq({ { 1, 10, 1, 13 } }, ret)
|
eq({ { 1, 10, 1, 13 } }, ret)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('iter_captures supports columns', function()
|
||||||
|
local txt = [[
|
||||||
|
int aaa = 1, bbb = 2;
|
||||||
|
int foo = 1, bar = 2;
|
||||||
|
int baz = 3, qux = 4;
|
||||||
|
int ccc = 1, ddd = 2;
|
||||||
|
]]
|
||||||
|
|
||||||
|
local function test(opts)
|
||||||
|
local parser = vim.treesitter.get_string_parser(txt, 'c')
|
||||||
|
|
||||||
|
local nodes = {}
|
||||||
|
local query = vim.treesitter.query.parse('c', '((identifier) @foo)')
|
||||||
|
local root = assert(parser:parse()[1]:root())
|
||||||
|
local iter = query:iter_captures(root, txt, 1, 2, opts)
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local capture, node = iter()
|
||||||
|
if not capture then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insert(nodes, { node:range() })
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret
|
||||||
|
ret = exec_lua(test, { col_begin = 7, col_end = 13 })
|
||||||
|
eq({ { 1, 13, 1, 16 }, { 2, 4, 2, 7 } }, ret)
|
||||||
|
|
||||||
|
ret = exec_lua(test, { col_begin = 7 })
|
||||||
|
eq({ { 1, 13, 1, 16 } }, ret)
|
||||||
|
|
||||||
|
ret = exec_lua(test, { col_end = 13 })
|
||||||
|
eq({ { 1, 4, 1, 7 }, { 1, 13, 1, 16 }, { 2, 4, 2, 7 } }, ret)
|
||||||
|
|
||||||
|
ret = exec_lua(test, {})
|
||||||
|
eq({ { 1, 4, 1, 7 }, { 1, 13, 1, 16 } }, ret)
|
||||||
|
end)
|
||||||
|
|
||||||
it('fails to load queries', function()
|
it('fails to load queries', function()
|
||||||
local function test(exp, cquery)
|
local function test(exp, cquery)
|
||||||
eq(exp, pcall_err(exec_lua, "vim.treesitter.query.parse('c', ...)", cquery))
|
eq(exp, pcall_err(exec_lua, "vim.treesitter.query.parse('c', ...)", cquery))
|
||||||
|
@ -15,32 +15,36 @@ local eq = t.eq
|
|||||||
local assert_alive = n.assert_alive
|
local assert_alive = n.assert_alive
|
||||||
local pcall_err = t.pcall_err
|
local pcall_err = t.pcall_err
|
||||||
|
|
||||||
|
local function setup_screen(screen)
|
||||||
|
screen:set_default_attr_ids {
|
||||||
|
[1] = {bold=true, foreground=Screen.colors.Blue};
|
||||||
|
[2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red};
|
||||||
|
[3] = {foreground = Screen.colors.Brown};
|
||||||
|
[4] = {foreground = Screen.colors.Blue1};
|
||||||
|
[5] = {foreground = Screen.colors.Magenta};
|
||||||
|
[6] = {bold = true, foreground = Screen.colors.Brown};
|
||||||
|
[7] = {background = Screen.colors.Gray90};
|
||||||
|
[8] = {bold = true, reverse = true};
|
||||||
|
[9] = {reverse = true};
|
||||||
|
[10] = {italic = true, background = Screen.colors.Magenta};
|
||||||
|
[11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')};
|
||||||
|
[12] = {foreground = tonumber('0x990000')};
|
||||||
|
[13] = {background = Screen.colors.LightBlue};
|
||||||
|
[14] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue};
|
||||||
|
[15] = {special = Screen.colors.Blue, undercurl = true},
|
||||||
|
[16] = {special = Screen.colors.Red, undercurl = true},
|
||||||
|
[17] = {foreground = Screen.colors.Red},
|
||||||
|
[18] = {bold = true, foreground = Screen.colors.SeaGreen};
|
||||||
|
[19] = {bold = true};
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
describe('decorations providers', function()
|
describe('decorations providers', function()
|
||||||
local screen
|
local screen
|
||||||
before_each(function()
|
before_each(function()
|
||||||
clear()
|
clear()
|
||||||
screen = Screen.new(40, 8)
|
screen = Screen.new(40, 8)
|
||||||
screen:set_default_attr_ids {
|
setup_screen(screen)
|
||||||
[1] = {bold=true, foreground=Screen.colors.Blue};
|
|
||||||
[2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red};
|
|
||||||
[3] = {foreground = Screen.colors.Brown};
|
|
||||||
[4] = {foreground = Screen.colors.Blue1};
|
|
||||||
[5] = {foreground = Screen.colors.Magenta};
|
|
||||||
[6] = {bold = true, foreground = Screen.colors.Brown};
|
|
||||||
[7] = {background = Screen.colors.Gray90};
|
|
||||||
[8] = {bold = true, reverse = true};
|
|
||||||
[9] = {reverse = true};
|
|
||||||
[10] = {italic = true, background = Screen.colors.Magenta};
|
|
||||||
[11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')};
|
|
||||||
[12] = {foreground = tonumber('0x990000')};
|
|
||||||
[13] = {background = Screen.colors.LightBlue};
|
|
||||||
[14] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue};
|
|
||||||
[15] = {special = Screen.colors.Blue, undercurl = true},
|
|
||||||
[16] = {special = Screen.colors.Red, undercurl = true},
|
|
||||||
[17] = {foreground = Screen.colors.Red},
|
|
||||||
[18] = {bold = true, foreground = Screen.colors.SeaGreen};
|
|
||||||
[19] = {bold = true};
|
|
||||||
}
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local mulholland = [[
|
local mulholland = [[
|
||||||
@ -64,7 +68,7 @@ describe('decorations providers', function()
|
|||||||
]]) .. [[
|
]]) .. [[
|
||||||
api.nvim_set_decoration_provider(_G.ns1, {
|
api.nvim_set_decoration_provider(_G.ns1, {
|
||||||
on_start = on_do; on_buf = on_do;
|
on_start = on_do; on_buf = on_do;
|
||||||
on_win = on_do; on_line = on_do;
|
on_win = on_do; on_line = on_do; on_range = on_do;
|
||||||
on_end = on_do; _on_spell_nav = on_do;
|
on_end = on_do; _on_spell_nav = on_do;
|
||||||
})
|
})
|
||||||
return _G.ns1
|
return _G.ns1
|
||||||
@ -107,12 +111,19 @@ describe('decorations providers', function()
|
|||||||
{ "start", 4 };
|
{ "start", 4 };
|
||||||
{ "win", 1000, 1, 0, 6 };
|
{ "win", 1000, 1, 0, 6 };
|
||||||
{ "line", 1000, 1, 0 };
|
{ "line", 1000, 1, 0 };
|
||||||
|
{ "range", 1000, 1, 0, 0, 1, 0 };
|
||||||
{ "line", 1000, 1, 1 };
|
{ "line", 1000, 1, 1 };
|
||||||
|
{ "range", 1000, 1, 1, 0, 2, 0 };
|
||||||
{ "line", 1000, 1, 2 };
|
{ "line", 1000, 1, 2 };
|
||||||
|
{ "range", 1000, 1, 2, 0, 3, 0 };
|
||||||
{ "line", 1000, 1, 3 };
|
{ "line", 1000, 1, 3 };
|
||||||
|
{ "range", 1000, 1, 3, 0, 4, 0 };
|
||||||
{ "line", 1000, 1, 4 };
|
{ "line", 1000, 1, 4 };
|
||||||
|
{ "range", 1000, 1, 4, 0, 5, 0 };
|
||||||
{ "line", 1000, 1, 5 };
|
{ "line", 1000, 1, 5 };
|
||||||
|
{ "range", 1000, 1, 5, 0, 6, 0 };
|
||||||
{ "line", 1000, 1, 6 };
|
{ "line", 1000, 1, 6 };
|
||||||
|
{ "range", 1000, 1, 6, 0, 7, 0 };
|
||||||
{ "end", 4 };
|
{ "end", 4 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +143,7 @@ describe('decorations providers', function()
|
|||||||
{ "buf", 1, 5 };
|
{ "buf", 1, 5 };
|
||||||
{ "win", 1000, 1, 0, 6 };
|
{ "win", 1000, 1, 0, 6 };
|
||||||
{ "line", 1000, 1, 6 };
|
{ "line", 1000, 1, 6 };
|
||||||
|
{ "range", 1000, 1, 6, 0, 7, 0 };
|
||||||
{ "end", 5 };
|
{ "end", 5 };
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
@ -199,9 +211,13 @@ describe('decorations providers', function()
|
|||||||
{ "start", 5 };
|
{ "start", 5 };
|
||||||
{ "win", 1000, 1, 0, 3 };
|
{ "win", 1000, 1, 0, 3 };
|
||||||
{ "line", 1000, 1, 0 };
|
{ "line", 1000, 1, 0 };
|
||||||
|
{ "range", 1000, 1, 0, 0, 1, 0 };
|
||||||
{ "line", 1000, 1, 1 };
|
{ "line", 1000, 1, 1 };
|
||||||
|
{ "range", 1000, 1, 1, 0, 2, 0 };
|
||||||
{ "line", 1000, 1, 2 };
|
{ "line", 1000, 1, 2 };
|
||||||
|
{ "range", 1000, 1, 2, 0, 3, 0 };
|
||||||
{ "line", 1000, 1, 3 };
|
{ "line", 1000, 1, 3 };
|
||||||
|
{ "range", 1000, 1, 3, 0, 4, 0 };
|
||||||
{ "end", 5 };
|
{ "end", 5 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -694,6 +710,83 @@ describe('decorations providers', function()
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('on_range is invoked on all visible characters', function()
|
||||||
|
clear()
|
||||||
|
screen = Screen.new(20, 4)
|
||||||
|
setup_screen(screen)
|
||||||
|
|
||||||
|
local function record()
|
||||||
|
exec_lua(function()
|
||||||
|
_G.p_min = { math.huge, math.huge }
|
||||||
|
_G.p_max = { -math.huge, -math.huge }
|
||||||
|
function _G.pos_gt(a, b)
|
||||||
|
return a[1] > b[1] or (a[1] == b[1] and a[2] > b[2])
|
||||||
|
end
|
||||||
|
function _G.pos_lt(a, b)
|
||||||
|
return a[1] < b[1] or (a[1] == b[1] and a[2] < b[2])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
setup_provider [[
|
||||||
|
local function on_do(kind, _, bufnr, br, bc, er, ec)
|
||||||
|
if kind == 'range' then
|
||||||
|
local b = { br, bc }
|
||||||
|
local e = { er, ec }
|
||||||
|
if _G.pos_gt(_G.p_min, b) then
|
||||||
|
_G.p_min = b
|
||||||
|
end
|
||||||
|
if _G.pos_lt(_G.p_max, e) then
|
||||||
|
_G.p_max = e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
]]
|
||||||
|
end
|
||||||
|
local function check(min, max)
|
||||||
|
local p_min = exec_lua('return _G.p_min')
|
||||||
|
assert(
|
||||||
|
p_min[1] < min[1] or (p_min[1] == min[1] and p_min[2] <= min[2]),
|
||||||
|
"minimum position " .. vim.inspect(p_min)
|
||||||
|
.. " should be before the first char"
|
||||||
|
)
|
||||||
|
local p_max = exec_lua('return _G.p_max')
|
||||||
|
assert(
|
||||||
|
p_max[1] > max[1] or (p_max[1] == max[1] and p_max[2] >= max[2]),
|
||||||
|
"maximum position " .. vim.inspect(p_max)
|
||||||
|
.. " should be on or after the last char"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Multiple lines.
|
||||||
|
exec_lua([[
|
||||||
|
local lines = { ('a'):rep(40), ('b'):rep(40), ('c'):rep(40) }
|
||||||
|
vim.api.nvim_buf_set_lines(0, 0, -1, true, lines)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 2, 0 })
|
||||||
|
]])
|
||||||
|
record()
|
||||||
|
screen:expect([[
|
||||||
|
^bbbbbbbbbbbbbbbbbbbb|
|
||||||
|
bbbbbbbbbbbbbbbbbbbb|
|
||||||
|
ccccccccccccccccc{1:@@@}|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
check({ 1, 0 }, { 2, 21 })
|
||||||
|
|
||||||
|
-- One long line.
|
||||||
|
exec_lua([[
|
||||||
|
local lines = { ('a'):rep(100) }
|
||||||
|
vim.api.nvim_buf_set_lines(0, 0, -1, true, lines)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 1, 70 })
|
||||||
|
]])
|
||||||
|
record()
|
||||||
|
screen:expect([[
|
||||||
|
{1:<<<}aaaaaaaaaaaaaaaaa|
|
||||||
|
aaaaaaaaaaaaaaaaaaaa|
|
||||||
|
aaaaaaaaaa^aaaaaaaaaa|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
check({ 0, 20 }, { 0, 81 })
|
||||||
|
end)
|
||||||
|
|
||||||
it('errors gracefully', function()
|
it('errors gracefully', function()
|
||||||
insert(mulholland)
|
insert(mulholland)
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ describe('ui mode_change event', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- oldtest: Test_indent_norm_with_gq()
|
-- oldtest: Test_mouse_shape_indent_norm_with_gq()
|
||||||
it('is restored to Normal mode after "gq" indents using :normal #12309', function()
|
it('is restored to Normal mode after "gq" indents using :normal #12309', function()
|
||||||
screen:try_resize(60, 6)
|
screen:try_resize(60, 6)
|
||||||
n.exec([[
|
n.exec([[
|
||||||
|
@ -1162,6 +1162,8 @@ describe('builtin popupmenu', function()
|
|||||||
[6] = { foreground = Screen.colors.White, background = Screen.colors.Red },
|
[6] = { foreground = Screen.colors.White, background = Screen.colors.Red },
|
||||||
[7] = { background = Screen.colors.Yellow }, -- Search
|
[7] = { background = Screen.colors.Yellow }, -- Search
|
||||||
[8] = { foreground = Screen.colors.Red },
|
[8] = { foreground = Screen.colors.Red },
|
||||||
|
[9] = { foreground = Screen.colors.Yellow, background = Screen.colors.Green },
|
||||||
|
[10] = { foreground = Screen.colors.White, background = Screen.colors.Green },
|
||||||
ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey },
|
ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey },
|
||||||
kn = { foreground = Screen.colors.Red, background = Screen.colors.Plum1 },
|
kn = { foreground = Screen.colors.Red, background = Screen.colors.Plum1 },
|
||||||
xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey },
|
xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey },
|
||||||
@ -5625,6 +5627,114 @@ describe('builtin popupmenu', function()
|
|||||||
{2:-- INSERT --} |
|
{2:-- INSERT --} |
|
||||||
]])
|
]])
|
||||||
feed('<Esc>')
|
feed('<Esc>')
|
||||||
|
|
||||||
|
-- text after the inserted text shouldn't be highlighted
|
||||||
|
feed('0ea <C-X><C-O>')
|
||||||
|
screen:expect([[
|
||||||
|
αβγ {8:foo}^ foo |
|
||||||
|
{1:~ }{s: foo }{1: }|
|
||||||
|
{1:~ }{n: bar }{1: }|
|
||||||
|
{1:~ }{n: 你好 }{1: }|
|
||||||
|
{1:~ }|*15
|
||||||
|
{2:-- }{5:match 1 of 3} |
|
||||||
|
]])
|
||||||
|
feed('<C-P>')
|
||||||
|
screen:expect([[
|
||||||
|
αβγ ^ foo |
|
||||||
|
{1:~ }{n: foo }{1: }|
|
||||||
|
{1:~ }{n: bar }{1: }|
|
||||||
|
{1:~ }{n: 你好 }{1: }|
|
||||||
|
{1:~ }|*15
|
||||||
|
{2:-- }{8:Back at original} |
|
||||||
|
]])
|
||||||
|
feed('<C-P>')
|
||||||
|
screen:expect([[
|
||||||
|
αβγ {8:你好}^ foo |
|
||||||
|
{1:~ }{n: foo }{1: }|
|
||||||
|
{1:~ }{n: bar }{1: }|
|
||||||
|
{1:~ }{s: 你好 }{1: }|
|
||||||
|
{1:~ }|*15
|
||||||
|
{2:-- }{5:match 3 of 3} |
|
||||||
|
]])
|
||||||
|
feed('<C-Y>')
|
||||||
|
screen:expect([[
|
||||||
|
αβγ 你好^ foo |
|
||||||
|
{1:~ }|*18
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]])
|
||||||
|
feed('<Esc>')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- oldtest: Test_pum_matchins_highlight_combine()
|
||||||
|
it('with ComplMatchIns, Normal and CursorLine highlights', function()
|
||||||
|
exec([[
|
||||||
|
func Omni_test(findstart, base)
|
||||||
|
if a:findstart
|
||||||
|
return col(".")
|
||||||
|
endif
|
||||||
|
return [#{word: "foo"}, #{word: "bar"}, #{word: "你好"}]
|
||||||
|
endfunc
|
||||||
|
set omnifunc=Omni_test
|
||||||
|
hi Normal guibg=blue
|
||||||
|
hi CursorLine guibg=green guifg=white
|
||||||
|
set cursorline
|
||||||
|
call setline(1, 'aaa bbb')
|
||||||
|
]])
|
||||||
|
|
||||||
|
-- when ComplMatchIns is not set, CursorLine applies normally
|
||||||
|
feed('0ea <C-X><C-O>')
|
||||||
|
screen:expect([[
|
||||||
|
{10:aaa foo^ bbb }|
|
||||||
|
{1:~ }{s: foo }{1: }|
|
||||||
|
{1:~ }{n: bar }{1: }|
|
||||||
|
{1:~ }{n: 你好 }{1: }|
|
||||||
|
{1:~ }|*15
|
||||||
|
{2:-- }{5:match 1 of 3} |
|
||||||
|
]])
|
||||||
|
feed('<C-E>')
|
||||||
|
screen:expect([[
|
||||||
|
{10:aaa ^ bbb }|
|
||||||
|
{1:~ }|*18
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]])
|
||||||
|
feed('<BS><Esc>')
|
||||||
|
|
||||||
|
-- when ComplMatchIns is set, it is applied over CursorLine
|
||||||
|
command('hi ComplMatchIns guifg=Yellow')
|
||||||
|
feed('0ea <C-X><C-O>')
|
||||||
|
screen:expect([[
|
||||||
|
{10:aaa }{9:foo}{10:^ bbb }|
|
||||||
|
{1:~ }{s: foo }{1: }|
|
||||||
|
{1:~ }{n: bar }{1: }|
|
||||||
|
{1:~ }{n: 你好 }{1: }|
|
||||||
|
{1:~ }|*15
|
||||||
|
{2:-- }{5:match 1 of 3} |
|
||||||
|
]])
|
||||||
|
feed('<C-P>')
|
||||||
|
screen:expect([[
|
||||||
|
{10:aaa ^ bbb }|
|
||||||
|
{1:~ }{n: foo }{1: }|
|
||||||
|
{1:~ }{n: bar }{1: }|
|
||||||
|
{1:~ }{n: 你好 }{1: }|
|
||||||
|
{1:~ }|*15
|
||||||
|
{2:-- }{8:Back at original} |
|
||||||
|
]])
|
||||||
|
feed('<C-P>')
|
||||||
|
screen:expect([[
|
||||||
|
{10:aaa }{9:你好}{10:^ bbb }|
|
||||||
|
{1:~ }{n: foo }{1: }|
|
||||||
|
{1:~ }{n: bar }{1: }|
|
||||||
|
{1:~ }{s: 你好 }{1: }|
|
||||||
|
{1:~ }|*15
|
||||||
|
{2:-- }{5:match 3 of 3} |
|
||||||
|
]])
|
||||||
|
feed('<C-E>')
|
||||||
|
screen:expect([[
|
||||||
|
{10:aaa ^ bbb }|
|
||||||
|
{1:~ }|*18
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]])
|
||||||
|
feed('<Esc>')
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -308,7 +308,7 @@ endfunc
|
|||||||
|
|
||||||
" Test that mouse shape is restored to Normal mode after using "gq" when
|
" Test that mouse shape is restored to Normal mode after using "gq" when
|
||||||
" 'indentexpr' executes :normal.
|
" 'indentexpr' executes :normal.
|
||||||
func Test_indent_norm_with_gq()
|
func Test_mouse_shape_indent_norm_with_gq()
|
||||||
CheckFeature mouseshape
|
CheckFeature mouseshape
|
||||||
CheckCanRunGui
|
CheckCanRunGui
|
||||||
|
|
||||||
|
@ -1747,13 +1747,67 @@ func Test_pum_matchins_highlight()
|
|||||||
call TermWait(buf)
|
call TermWait(buf)
|
||||||
call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<C-Y>")
|
call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<C-Y>")
|
||||||
call VerifyScreenDump(buf, 'Test_pum_matchins_04', {})
|
call VerifyScreenDump(buf, 'Test_pum_matchins_04', {})
|
||||||
call term_sendkeys(buf, "\<C-E>\<Esc>")
|
call term_sendkeys(buf, "\<Esc>")
|
||||||
|
|
||||||
" restore after cancel completion
|
" restore after cancel completion
|
||||||
call TermWait(buf)
|
call TermWait(buf)
|
||||||
call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<Space>")
|
call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<Space>")
|
||||||
call VerifyScreenDump(buf, 'Test_pum_matchins_05', {})
|
call VerifyScreenDump(buf, 'Test_pum_matchins_05', {})
|
||||||
call term_sendkeys(buf, "\<C-E>\<Esc>")
|
call term_sendkeys(buf, "\<Esc>")
|
||||||
|
|
||||||
|
" text after the inserted text shouldn't be highlighted
|
||||||
|
call TermWait(buf)
|
||||||
|
call term_sendkeys(buf, "0ea \<C-X>\<C-O>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_07', {})
|
||||||
|
call term_sendkeys(buf, "\<C-P>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_08', {})
|
||||||
|
call term_sendkeys(buf, "\<C-P>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_09', {})
|
||||||
|
call term_sendkeys(buf, "\<C-Y>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_10', {})
|
||||||
|
call term_sendkeys(buf, "\<Esc>")
|
||||||
|
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_pum_matchins_highlight_combine()
|
||||||
|
CheckScreendump
|
||||||
|
let lines =<< trim END
|
||||||
|
func Omni_test(findstart, base)
|
||||||
|
if a:findstart
|
||||||
|
return col(".")
|
||||||
|
endif
|
||||||
|
return [#{word: "foo"}, #{word: "bar"}, #{word: "你好"}]
|
||||||
|
endfunc
|
||||||
|
set omnifunc=Omni_test
|
||||||
|
hi Normal ctermbg=blue
|
||||||
|
hi CursorLine cterm=underline ctermbg=green
|
||||||
|
set cursorline
|
||||||
|
call setline(1, 'aaa bbb')
|
||||||
|
END
|
||||||
|
call writefile(lines, 'Xscript', 'D')
|
||||||
|
let buf = RunVimInTerminal('-S Xscript', {})
|
||||||
|
|
||||||
|
" when ComplMatchIns is not set, CursorLine applies normally
|
||||||
|
call term_sendkeys(buf, "0ea \<C-X>\<C-O>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_combine_01', {})
|
||||||
|
call term_sendkeys(buf, "\<C-E>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_combine_02', {})
|
||||||
|
call term_sendkeys(buf, "\<BS>\<Esc>")
|
||||||
|
|
||||||
|
" when ComplMatchIns is set, it is applied over CursorLine
|
||||||
|
call TermWait(buf)
|
||||||
|
call term_sendkeys(buf, ":hi ComplMatchIns ctermbg=red ctermfg=yellow\<CR>")
|
||||||
|
call TermWait(buf)
|
||||||
|
call term_sendkeys(buf, "0ea \<C-X>\<C-O>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_combine_03', {})
|
||||||
|
call term_sendkeys(buf, "\<C-P>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_combine_04', {})
|
||||||
|
call term_sendkeys(buf, "\<C-P>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_combine_05', {})
|
||||||
|
call term_sendkeys(buf, "\<C-E>")
|
||||||
|
call VerifyScreenDump(buf, 'Test_pum_matchins_combine_06', {})
|
||||||
|
call term_sendkeys(buf, "\<Esc>")
|
||||||
|
|
||||||
call StopVimInTerminal(buf)
|
call StopVimInTerminal(buf)
|
||||||
endfunc
|
endfunc
|
||||||
|
Loading…
Reference in New Issue
Block a user