Compare commits

...

16 Commits

Author SHA1 Message Date
Damien Mehala
a83188ff3a
Merge 4531c2a04e into 5792546777 2024-09-15 20:56:57 +08:00
Justin M. Keyes
5792546777
refactor(tests): rename terminal/testutil.lua => testterm.lua #30372
This module is generally used by any tests that need the full Nvim TUI
instead of `screen.lua`. Thus it should live in `functional/` instead of
in `functional/terminal/`.
2024-09-15 03:28:14 -07:00
zeertzjq
3b54adc6c6
Merge pull request #30378 from zeertzjq/vim-9.1.0729
vim-patch:9.1.{0729,0730}
2024-09-14 19:51:11 +08:00
zeertzjq
325d349f9d
vim-patch:9.1.0728: [security]: heap-use-after-free in garbage collection with location list user data (#30377)
Problem:  heap-use-after-free in garbage collection with location list
          user data.
Solution: Mark user data as in use when no other window is referencing
          the location list (zeertzjq)

fixes: neovim/neovim#30371
closes: vim/vim#15683

be4bd189d2
2024-09-14 19:38:33 +08:00
zeertzjq
5191a11d66 vim-patch:9.1.0730: Crash with cursor-screenline and narrow window
Problem:  Crash with cursor-screenline and narrow window
          (elig0n)
Solution: Don't set right_col when width2 is 0 (zeertzjq).

fixes: vim/vim#15677
closes: vim/vim#15678

59149f0269
2024-09-14 19:29:40 +08:00
zeertzjq
90585e47fe vim-patch:9.1.0729: Wrong cursor-screenline when resizing window
Problem:  Wrong cursor-screenline when resizing window
Solution: Invalidate saved left_col and right_col when width1 or width2
          change.

closes: vim/vim#15679

86dc4f8b43
2024-09-14 19:29:37 +08:00
Jaehwang Jung
f2173b1aa2
fix(defaults): cannot remove "How-to disable mouse" menu item #30375 2024-09-14 02:18:38 -07:00
dundargoc
67d6b6f27e ci: skip automerge step if backport failed 2024-09-14 01:55:36 +02:00
Mathias Fussenegger
8512f669f0 fix(lsp): handle nil bytes in strings
Problem:

The LSP omnifunc can insert nil bytes, which when read in other places
(like semantic token) could cause an error:

    semantic_tokens.lua:304: Vim:E976: Using a Blob as a String

Solution:

Use `#line` instead of `vim.fn.strlen(line)`. Both return UTF-8 bytes
but the latter can't handle nil bytes.

Completion candidates can currently insert nil bytes, if other parts of
Alternative fix to https://github.com/neovim/neovim/pull/30359

Note that https://github.com/neovim/neovim/pull/30315 will avoid the
insertion of nil bytes from the LSP omnifunc, but the change of this PR
can more easily be backported.
2024-09-13 22:34:49 +02:00
dundargoc
755512ed60 ci: don't add reviewers for PRs created by a bot
This will ensure automatic backports created by the backport action does
not request reviewers (since the commit in question has already been
vetted and merged), but manual backports created by users does request
reviewers as these commits has not been vetted previously.
2024-09-13 16:58:15 +02:00
dundargoc
4c23b83456 ci: add needs:backport label on backport PRs with conflict
This makes it easy to keep track of which backport PRs have failed and
need manual intervention to fix.
2024-09-13 16:49:35 +02:00
dundargoc
5284a2a793 build: bump unibilium to v2.1.2
There is no real practical difference from previous commit except that
this is a tagged release.
2024-09-13 15:56:37 +02:00
Riley Bruins
b9b408a56c
feat(treesitter): start moving get_parser to return nil #30313
**Problem:** `vim.treesitter.get_parser` will throw an error if no parser
can be found.

- This means the caller is responsible for wrapping it in a `pcall`,
  which is easy to forget
- It also makes it slightly harder to potentially memoize `get_parser`
  in the future
- It's a bit unintuitive since many other `get_*` style functions
  conventionally return `nil` if no object is found (e.g. `get_node`,
  `get_lang`, `query.get`, etc.)

**Solution:** Return `nil` if no parser can be found or created

- This requires a function signature change, and some new assertions in
  places where the parser will always (or should always) be found.
- This commit starts by making this change internally, since it is
  breaking. Eventually it will be rolled out to the public API.
2024-09-13 05:09:11 -07:00
James Trew
8654a97006
fix(lsp): handle empty call hierarchy items #30349
Ensure that the function `pick_call_hierarchy_item` correctly handles
the case where `call_hierarchy_items` is nil or an empty table. This
prevents potential errors when the function is called with no items.
2024-09-13 04:59:49 -07:00
dundargoc
057314345a ci: enable automerge by default when backporting
This will automatically merge backported PRs without human intervention
if the tests pass.
2024-09-13 12:31:33 +02:00
Damien MEHALA
4531c2a04e fix(treesitter): get_node_text() newling handling consistency
- Modified `buf_range_get_text` to insert the trailing newline char.
2024-05-18 22:02:17 +02:00
39 changed files with 310 additions and 176 deletions

View File

@ -26,3 +26,21 @@ jobs:
pull_title: "${pull_title}"
label_pattern: "^ci:backport ([^ ]+)$"
github_token: ${{ steps.app-token.outputs.token }}
- name: Create failed backport label
if: ${{ steps.backport.outputs.was_successful == 'false' }}
uses: actions/github-script@v7
with:
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['needs:backport']
})
- name: Enable automerge
if: ${{ steps.backport.outputs.was_successful == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --rebase --auto ${{ steps.backport.outputs.created_pull_numbers }}

View File

@ -5,7 +5,7 @@ on:
workflow_call:
jobs:
request-reviewer:
if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false
if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false && !endsWith(github.actor, '[bot]')
runs-on: ubuntu-latest
permissions:
pull-requests: write

View File

@ -7,8 +7,8 @@ LUAJIT_SHA256 2b5514bd6a6573cb6111b43d013e952cbaf46762d14ebe26c872ddb80b5a84e0
LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz
LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
UNIBILIUM_URL https://github.com/neovim/unibilium/archive/ab28a2ddb86a144194a798bcf1fef1ab4f981ab7.tar.gz
UNIBILIUM_SHA256 7c0386fc48452632868e0dcb8e7ed140d67a3da79e39ceee54c1ba7a495ad625
UNIBILIUM_URL https://github.com/neovim/unibilium/archive/v2.1.2.tar.gz
UNIBILIUM_SHA256 370ecb07fbbc20d91d1b350c55f1c806b06bf86797e164081ccc977fc9b3af7a
LUV_URL https://github.com/luvit/luv/releases/download/1.48.0-2/luv-1.48.0-2.tar.gz
LUV_SHA256 2c3a1ddfebb4f6550293a40ee789f7122e97647eede51511f57203de48c03b7a

View File

@ -9,8 +9,8 @@
local function get_commentstring(ref_position)
local buf_cs = vim.bo.commentstring
local has_ts_parser, ts_parser = pcall(vim.treesitter.get_parser)
if not has_ts_parser then
local ts_parser = vim.treesitter._get_parser()
if not ts_parser then
return buf_cs
end

View File

@ -213,23 +213,25 @@ end
--- Default menus
do
--- Right click popup menu
local function def_menu(ctx)
vim.cmd([[
anoremenu PopUp.Go\ to\ definition <Cmd>lua vim.lsp.buf.definition()<CR>
amenu PopUp.Open\ in\ web\ browser gx
anoremenu PopUp.Inspect <Cmd>Inspect<CR>
anoremenu PopUp.-1- <Nop>
vnoremenu PopUp.Cut "+x
vnoremenu PopUp.Copy "+y
anoremenu PopUp.Paste "+gP
vnoremenu PopUp.Paste "+P
vnoremenu PopUp.Delete "_x
nnoremenu PopUp.Select\ All ggVG
vnoremenu PopUp.Select\ All gg0oG$
inoremenu PopUp.Select\ All <C-Home><C-O>VG
anoremenu PopUp.-2- <Nop>
anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
vim.cmd([[
anoremenu PopUp.Go\ to\ definition <Cmd>lua vim.lsp.buf.definition()<CR>
amenu PopUp.Open\ in\ web\ browser gx
anoremenu PopUp.Inspect <Cmd>Inspect<CR>
anoremenu PopUp.-1- <Nop>
vnoremenu PopUp.Cut "+x
vnoremenu PopUp.Copy "+y
anoremenu PopUp.Paste "+gP
vnoremenu PopUp.Paste "+P
vnoremenu PopUp.Delete "_x
nnoremenu PopUp.Select\ All ggVG
vnoremenu PopUp.Select\ All gg0oG$
inoremenu PopUp.Select\ All <C-Home><C-O>VG
anoremenu PopUp.-2- <Nop>
anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
]])
local function enable_ctx_menu(ctx)
vim.cmd([[
amenu disable PopUp.Go\ to\ definition
amenu disable PopUp.Open\ in\ web\ browser
]])
@ -240,7 +242,6 @@ do
vim.cmd([[anoremenu enable PopUp.Go\ to\ definition]])
end
end
def_menu()
local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim_popupmenu', {})
vim.api.nvim_create_autocmd('MenuPopup', {
@ -252,7 +253,7 @@ do
local urls = require('vim.ui')._get_urls()
local url = vim.startswith(urls[1], 'http')
local ctx = url and 'url' or (vim.lsp.get_clients({ bufnr = 0 })[1] and 'lsp' or nil)
def_menu(ctx)
enable_ctx_menu(ctx)
end,
})
end

View File

@ -447,11 +447,9 @@ function M.document_symbol(opts)
request_with_opts(ms.textDocument_documentSymbol, params, opts)
end
--- @param call_hierarchy_items lsp.CallHierarchyItem[]?
--- @param call_hierarchy_items lsp.CallHierarchyItem[]
--- @return lsp.CallHierarchyItem?
local function pick_call_hierarchy_item(call_hierarchy_items)
if not call_hierarchy_items then
return
end
if #call_hierarchy_items == 1 then
return call_hierarchy_items[1]
end
@ -476,7 +474,7 @@ local function call_hierarchy(method)
vim.notify(err.message, vim.log.levels.WARN)
return
end
if not result then
if not result or vim.tbl_isempty(result) then
vim.notify('No item resolved', vim.log.levels.WARN)
return
end

View File

@ -154,7 +154,7 @@ end
---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16
---@return integer byte (utf-8) index of `encoding` index `index` in `line`
function M._str_byteindex_enc(line, index, encoding)
local len = vim.fn.strlen(line)
local len = #line
if index > len then
-- LSP spec: if character > line length, default to the line length.
-- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position
@ -167,7 +167,7 @@ function M._str_byteindex_enc(line, index, encoding)
if index then
return index
else
return #line
return len
end
elseif encoding == 'utf-16' then
return vim.str_byteindex(line, index, true)

View File

@ -74,14 +74,14 @@ end
--- Returns the parser for a specific buffer and attaches it to the buffer
---
--- If needed, this will create the parser.
--- If needed, this will create the parser. If no parser can be found or created, returns `nil`.
---
---@param bufnr (integer|nil) Buffer the parser should be tied to (default: current buffer)
---@param lang (string|nil) Language of this parser (default: from buffer filetype)
---@param opts (table|nil) Options to pass to the created language tree
---
---@return vim.treesitter.LanguageTree object to use for parsing
function M.get_parser(bufnr, lang, opts)
---@return vim.treesitter.LanguageTree? object to use for parsing, or `nil` if not found
function M._get_parser(bufnr, lang, opts)
opts = opts or {}
if bufnr == nil or bufnr == 0 then
@ -94,18 +94,14 @@ function M.get_parser(bufnr, lang, opts)
if not valid_lang(lang) then
if not parsers[bufnr] then
error(
string.format(
'There is no parser available for buffer %d and one could not be'
.. ' created because lang could not be determined. Either pass lang'
.. ' or set the buffer filetype',
bufnr
)
)
return nil
end
elseif parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
assert(lang, 'lang should be valid')
parsers[bufnr] = M._create_parser(bufnr, lang, opts)
local parser = vim.F.npcall(M._create_parser, bufnr, lang, opts)
if not parser then
return nil
end
parsers[bufnr] = parser
end
parsers[bufnr]:register_cbs(opts.buf_attach_cbs)
@ -113,6 +109,29 @@ function M.get_parser(bufnr, lang, opts)
return parsers[bufnr]
end
--- Returns the parser for a specific buffer and attaches it to the buffer
---
--- If needed, this will create the parser.
---
---@param bufnr (integer|nil) Buffer the parser should be tied to (default: current buffer)
---@param lang (string|nil) Language of this parser (default: from buffer filetype)
---@param opts (table|nil) Options to pass to the created language tree
---
---@return vim.treesitter.LanguageTree object to use for parsing
function M.get_parser(bufnr, lang, opts)
-- TODO(ribru17): Remove _get_parser and move that logic back here once the breaking function
-- signature change is acceptable.
local parser = M._get_parser(bufnr, lang, opts)
if not parser then
vim.notify_once(
'WARNING: vim.treesitter.get_parser will return nil instead of raising an error in Neovim 0.12',
vim.log.levels.WARN
)
error('Parser not found.')
end
return parser
end
--- Returns a string parser
---
---@param str string Text to parse
@ -187,6 +206,7 @@ end
---@returns string
local function buf_range_get_text(buf, range)
local start_row, start_col, end_row, end_col = M._range.unpack4(range)
local insert_nl = false
if end_col == 0 then
if start_row == end_row then
start_col = -1
@ -194,8 +214,12 @@ local function buf_range_get_text(buf, range)
end
end_col = -1
end_row = end_row - 1
insert_nl = true
end
local lines = api.nvim_buf_get_text(buf, start_row, start_col, end_row, end_col, {})
if insert_nl then
table.insert(lines, "")
end
return table.concat(lines, '\n')
end
@ -386,7 +410,7 @@ function M.get_node(opts)
local ts_range = { row, col, row, col }
local root_lang_tree = M.get_parser(bufnr, opts.lang)
local root_lang_tree = M._get_parser(bufnr, opts.lang)
if not root_lang_tree then
return
end
@ -419,7 +443,11 @@ end
---@param lang (string|nil) Language of the parser (default: from buffer filetype)
function M.start(bufnr, lang)
bufnr = bufnr or api.nvim_get_current_buf()
local parser = M.get_parser(bufnr, lang)
local parser = M._get_parser(bufnr, lang)
if not parser then
vim.notify('No parser for the given buffer.', vim.log.levels.WARN)
return
end
M.highlighter.new(parser)
end

View File

@ -114,7 +114,7 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections)
srow = srow or 0
erow = erow or api.nvim_buf_line_count(bufnr)
local parser = ts.get_parser(bufnr)
local parser = assert(ts._get_parser(bufnr))
parser:parse(parse_injections and { srow, erow } or nil)
@ -392,7 +392,7 @@ function M.foldexpr(lnum)
lnum = lnum or vim.v.lnum
local bufnr = api.nvim_get_current_buf()
local parser = vim.F.npcall(ts.get_parser, bufnr)
local parser = ts._get_parser(bufnr)
if not parser then
return '0'
end

View File

@ -172,7 +172,7 @@ function M.lint(buf, opts)
--- @type (table|nil)
local parser_info = vim.F.npcall(vim.treesitter.language.inspect, lang)
local parser = vim.treesitter.get_parser(buf)
local parser = assert(vim.treesitter._get_parser(buf), 'query parser not found.')
parser:parse()
parser:for_each_tree(function(tree, ltree)
if ltree:lang() == 'query' then

View File

@ -76,10 +76,9 @@ end
---
---@package
function TSTreeView:new(bufnr, lang)
local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang)
if not ok then
local err = parser --[[ @as string ]]
return nil, 'No parser available for the given buffer:\n' .. err
local parser = vim.treesitter._get_parser(bufnr or 0, lang)
if not parser then
return nil, 'No parser available for the given buffer.'
end
-- For each child tree (injected language), find the root of the tree and locate the node within
@ -539,7 +538,7 @@ local edit_ns = api.nvim_create_namespace('treesitter/dev-edit')
local function update_editor_highlights(query_win, base_win, lang)
local base_buf = api.nvim_win_get_buf(base_win)
local query_buf = api.nvim_win_get_buf(query_win)
local parser = vim.treesitter.get_parser(base_buf, lang)
local parser = assert(vim.treesitter._get_parser(base_buf, lang))
api.nvim_buf_clear_namespace(base_buf, edit_ns, 0, -1)
local query_content = table.concat(api.nvim_buf_get_lines(query_buf, 0, -1, false), '\n')
@ -596,8 +595,8 @@ function M.edit_query(lang)
end
vim.cmd(cmd)
local ok, parser = pcall(vim.treesitter.get_parser, buf, lang)
if not ok then
local parser = vim.treesitter._get_parser(buf, lang)
if not parser then
return nil, 'No parser available for the given buffer'
end
lang = parser:lang()

View File

@ -33,7 +33,7 @@ end
--- Show a table of contents for the help buffer in a loclist
function M.show_toc()
local bufnr = vim.api.nvim_get_current_buf()
local parser = vim.treesitter.get_parser(bufnr, 'vimdoc')
local parser = assert(vim.treesitter._get_parser(bufnr, 'vimdoc'), 'vimdoc parser not found.')
local query = vim.treesitter.query.parse(
parser:lang(),
[[

View File

@ -786,7 +786,7 @@ local function parse_buf(fname, parser_path)
if parser_path then
vim.treesitter.language.add('vimdoc', { path = parser_path })
end
local lang_tree = vim.treesitter.get_parser(buf)
local lang_tree = assert(vim.treesitter._get_parser(buf), 'vimdoc parser not found.')
return lang_tree, buf
end

View File

@ -170,28 +170,26 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
// cache previous calculations depending on w_virtcol
static int saved_w_virtcol;
static win_T *prev_wp;
static int prev_width1;
static int prev_width2;
static int prev_left_col;
static int prev_right_col;
static int prev_col_off;
int cur_col_off = win_col_off(wp);
int width1;
int width2;
int width1 = wp->w_width_inner - cur_col_off;
int width2 = width1 + win_col_off2(wp);
if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp
&& prev_col_off == cur_col_off) {
&& prev_width1 == width1 && prev_width2 == width2) {
*right_col = prev_right_col;
*left_col = prev_left_col;
return;
}
width1 = wp->w_width_inner - cur_col_off;
width2 = width1 + win_col_off2(wp);
*left_col = 0;
*right_col = width1;
if (wp->w_virtcol >= (colnr_T)width1) {
if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
*right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2;
}
if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
@ -202,8 +200,9 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
prev_left_col = *left_col;
prev_right_col = *right_col;
prev_wp = wp;
prev_width1 = width1;
prev_width2 = width2;
saved_w_virtcol = wp->w_virtcol;
prev_col_off = cur_col_off;
}
/// Put a single char from an UTF-8 buffer into a line buffer.

View File

@ -6875,7 +6875,8 @@ bool set_ref_in_quickfix(int copyID)
// In a location list window and none of the other windows is
// referring to this location list. Mark the location list
// context as still in use.
if (mark_quickfix_ctx(win->w_llist_ref, copyID)) {
if (mark_quickfix_ctx(win->w_llist_ref, copyID)
|| mark_quickfix_user_data(win->w_llist_ref, copyID)) {
return true;
}
}

View File

@ -114,6 +114,7 @@ function Session:request(method, ...)
return true, result
end
--- Runs the event loop.
function Session:run(request_cb, notification_cb, setup_cb, timeout)
local function on_request(method, args, response)
coroutine_exec(request_cb, method, args, function(status, result, flag)

View File

@ -1,6 +1,6 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local clear = n.clear
local feed_command = n.feed_command

View File

@ -1,6 +1,6 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local uv = vim.uv
local clear, command, testprg = n.clear, n.command, n.testprg
@ -199,7 +199,7 @@ end)
describe('autocmd TextChangedT', function()
clear()
local screen = tt.screen_setup()
local screen = tt.setup_screen()
it('works', function()
command('autocmd TextChangedT * ++once let g:called = 1')

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local clear = n.clear
local eq = t.eq

View File

@ -1,6 +1,6 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local assert_log = t.assert_log
local clear = n.clear

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local clear, eq, api = n.clear, t.eq, n.api
local feed = n.feed
local feed_data = tt.feed_data
@ -17,7 +17,7 @@ describe(':terminal altscreen', function()
before_each(function()
clear()
screen = tt.screen_setup()
screen = tt.setup_screen()
feed_data({
'line1',
'line2',

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local ok = t.ok
if t.skip(t.is_os('win')) then

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local assert_alive = n.assert_alive
local feed, clear = n.feed, n.clear
@ -29,7 +29,7 @@ describe(':terminal buffer', function()
before_each(function()
clear()
command('set modifiable swapfile undolevels=20')
screen = tt.screen_setup()
screen = tt.setup_screen()
end)
it('terminal-mode forces various options', function()
@ -574,7 +574,7 @@ if is_os('win') then
feed_command('set modifiable swapfile undolevels=20')
poke_eventloop()
local cmd = { 'cmd.exe', '/K', 'PROMPT=$g$s' }
screen = tt.screen_setup(nil, cmd)
screen = tt.setup_screen(nil, cmd)
end)
it('"put" operator sends data normally', function()

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local feed, clear = n.feed, n.clear
local testprg, command = n.testprg, n.command
@ -18,7 +18,7 @@ describe(':terminal cursor', function()
before_each(function()
clear()
screen = tt.screen_setup()
screen = tt.setup_screen()
end)
it('moves the screen cursor when focused', function()

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local feed, clear = n.feed, n.clear
local api = n.api

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local clear, eq, eval = n.clear, t.eq, n.eval
local feed, api, command = n.feed, n.api, n.command
local feed_data = tt.feed_data
@ -14,7 +14,7 @@ describe(':terminal mouse', function()
before_each(function()
clear()
api.nvim_set_option_value('statusline', '==========', {})
screen = tt.screen_setup()
screen = tt.setup_screen()
command('highlight StatusLine NONE')
command('highlight StatusLineNC NONE')
command('highlight StatusLineTerm NONE')

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local clear, eq = n.clear, t.eq
local feed, testprg = n.feed, n.testprg
@ -22,7 +22,7 @@ describe(':terminal scrollback', function()
before_each(function()
clear()
screen = tt.screen_setup(nil, nil, 30)
screen = tt.setup_screen(nil, nil, 30)
end)
describe('when the limit is exceeded', function()
@ -399,9 +399,9 @@ describe("'scrollback' option", function()
it('set to 0 behaves as 1', function()
local screen
if is_os('win') then
screen = tt.screen_setup(nil, { 'cmd.exe' }, 30)
screen = tt.setup_screen(nil, { 'cmd.exe' }, 30)
else
screen = tt.screen_setup(nil, { 'sh' }, 30)
screen = tt.setup_screen(nil, { 'sh' }, 30)
end
api.nvim_set_option_value('scrollback', 0, {})
@ -416,10 +416,10 @@ describe("'scrollback' option", function()
local screen
if is_os('win') then
command([[let $PROMPT='$$']])
screen = tt.screen_setup(nil, { 'cmd.exe' }, 30)
screen = tt.setup_screen(nil, { 'cmd.exe' }, 30)
else
command('let $PS1 = "$"')
screen = tt.screen_setup(nil, { 'sh' }, 30)
screen = tt.setup_screen(nil, { 'sh' }, 30)
end
api.nvim_set_option_value('scrollback', 200, {})
@ -480,8 +480,8 @@ describe("'scrollback' option", function()
end)
it('deletes extra lines immediately', function()
-- Scrollback is 10 on screen_setup
local screen = tt.screen_setup(nil, nil, 30)
-- Scrollback is 10 on setup_screen
local screen = tt.setup_screen(nil, nil, 30)
local lines = {}
for i = 1, 30 do
table.insert(lines, 'line' .. tostring(i))

View File

@ -7,7 +7,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local eq = t.eq
local feed_data = tt.feed_data
@ -2111,7 +2111,7 @@ describe('TUI', function()
finally(function()
os.remove('testF')
end)
local screen = tt.screen_setup(
local screen = tt.setup_screen(
0,
('"%s" -u NONE -i NONE --cmd "set noswapfile noshowcmd noruler" --cmd "normal iabc" > /dev/null 2>&1 && cat testF && rm testF'):format(
nvim_prog

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local feed_data = tt.feed_data
local feed, clear = n.feed, n.clear
local poke_eventloop = n.poke_eventloop
@ -37,7 +37,7 @@ describe(':terminal window', function()
before_each(function()
clear()
screen = tt.screen_setup()
screen = tt.setup_screen()
end)
it('sets topline correctly #8556', function()
@ -198,7 +198,7 @@ describe(':terminal with multigrid', function()
before_each(function()
clear()
screen = tt.screen_setup(0, nil, 50, nil, { ext_multigrid = true })
screen = tt.setup_screen(0, nil, 50, nil, { ext_multigrid = true })
end)
it('resizes to requested size', function()

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local assert_alive = n.assert_alive
local clear = n.clear
local feed = n.feed
@ -22,7 +22,7 @@ describe(':terminal', function()
-- set the statusline to a constant value because of variables like pid
-- and current directory and to improve visibility of splits
api.nvim_set_option_value('statusline', '==========', {})
screen = tt.screen_setup(3)
screen = tt.setup_screen(3)
command('highlight StatusLine NONE')
command('highlight StatusLineNC NONE')
command('highlight StatusLineTerm NONE')

View File

@ -250,6 +250,8 @@ function M.set_method_error(err)
method_error = err
end
--- Runs the event loop of the given session.
---
--- @param lsession test.Session
--- @param request_cb function?
--- @param notification_cb function?
@ -296,6 +298,7 @@ function M.run_session(lsession, request_cb, notification_cb, setup_cb, timeout)
return lsession.eof_err
end
--- Runs the event loop of the current global session.
function M.run(request_cb, notification_cb, setup_cb, timeout)
assert(session)
return M.run_session(session, request_cb, notification_cb, setup_cb, timeout)

View File

@ -1,6 +1,13 @@
-- To test tui/input.c, this module spawns `nvim` inside :terminal and sends
-- bytes via jobsend(). Note: the functional/testutil.lua test-session methods
-- operate on the _host_ session, _not_ the child session.
-- Functions to test :terminal and the Nvim TUI.
-- Starts a child process in a `:terminal` and sends bytes to the child via nvim_chan_send().
-- Note: the global functional/testutil.lua test-session is _host_ session, _not_
-- the child session.
--
-- - Use `setup_screen()` to test `:terminal` behavior with an arbitrary command.
-- - Use `setup_child_nvim()` to test the Nvim TUI.
-- - NOTE: Only use this if your test actually needs the full lifecycle/capabilities of the
-- builtin Nvim TUI. Most tests should just use `Screen.new()` directly, or plain old API calls.
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
@ -9,18 +16,20 @@ local exec_lua = n.exec_lua
local api = n.api
local nvim_prog = n.nvim_prog
local function feed_data(data)
local M = {}
function M.feed_data(data)
if type(data) == 'table' then
data = table.concat(data, '\n')
end
exec_lua('vim.api.nvim_chan_send(vim.b.terminal_job_id, ...)', data)
end
local function feed_termcode(data)
feed_data('\027' .. data)
function M.feed_termcode(data)
M.feed_data('\027' .. data)
end
local function make_lua_executor(session)
function M.make_lua_executor(session)
return function(code, ...)
local status, rv = session:request('nvim_exec_lua', code, { ... })
if not status then
@ -34,60 +43,68 @@ end
-- some t for controlling the terminal. the codes were taken from
-- infocmp xterm-256color which is less what libvterm understands
-- civis/cnorm
local function hide_cursor()
feed_termcode('[?25l')
function M.hide_cursor()
M.feed_termcode('[?25l')
end
local function show_cursor()
feed_termcode('[?25h')
function M.show_cursor()
M.feed_termcode('[?25h')
end
-- smcup/rmcup
local function enter_altscreen()
feed_termcode('[?1049h')
function M.enter_altscreen()
M.feed_termcode('[?1049h')
end
local function exit_altscreen()
feed_termcode('[?1049l')
function M.exit_altscreen()
M.feed_termcode('[?1049l')
end
-- character attributes
local function set_fg(num)
feed_termcode('[38;5;' .. num .. 'm')
function M.set_fg(num)
M.feed_termcode('[38;5;' .. num .. 'm')
end
local function set_bg(num)
feed_termcode('[48;5;' .. num .. 'm')
function M.set_bg(num)
M.feed_termcode('[48;5;' .. num .. 'm')
end
local function set_bold()
feed_termcode('[1m')
function M.set_bold()
M.feed_termcode('[1m')
end
local function set_italic()
feed_termcode('[3m')
function M.set_italic()
M.feed_termcode('[3m')
end
local function set_underline()
feed_termcode('[4m')
function M.set_underline()
M.feed_termcode('[4m')
end
local function set_underdouble()
feed_termcode('[4:2m')
function M.set_underdouble()
M.feed_termcode('[4:2m')
end
local function set_undercurl()
feed_termcode('[4:3m')
function M.set_undercurl()
M.feed_termcode('[4:3m')
end
local function set_strikethrough()
feed_termcode('[9m')
function M.set_strikethrough()
M.feed_termcode('[9m')
end
local function clear_attrs()
feed_termcode('[0;10m')
function M.clear_attrs()
M.feed_termcode('[0;10m')
end
-- mouse
local function enable_mouse()
feed_termcode('[?1002h')
function M.enable_mouse()
M.feed_termcode('[?1002h')
end
local function disable_mouse()
feed_termcode('[?1002l')
function M.disable_mouse()
M.feed_termcode('[?1002l')
end
local default_command = { testprg('tty-test') }
local function screen_setup(extra_rows, command, cols, env, screen_opts)
--- Runs `cmd` in a :terminal, and returns a `Screen` object.
---
---@param extra_rows? integer Extra rows to add to the default screen.
---@param cmd? string|string[] Command to run in the terminal (default: `{ 'tty-test' }`)
---@param cols? integer Create screen with this many columns (default: 50)
---@param env? table Environment set on the `cmd` job.
---@param screen_opts? table Options for `Screen.new()`.
---@return test.functional.ui.screen # Screen attached to the global (not child) Nvim session.
function M.setup_screen(extra_rows, cmd, cols, env, screen_opts)
extra_rows = extra_rows and extra_rows or 0
command = command and command or default_command
cmd = cmd and cmd or default_command
cols = cols and cols or 50
api.nvim_command('highlight TermCursor cterm=reverse')
@ -120,7 +137,7 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts)
screen:attach(screen_opts or { rgb = false })
api.nvim_command('enew')
api.nvim_call_function('termopen', { command, env and { env = env } or nil })
api.nvim_call_function('termopen', { cmd, env and { env = env } or nil })
api.nvim_input('<CR>')
local vim_errmsg = api.nvim_eval('v:errmsg')
if vim_errmsg and '' ~= vim_errmsg then
@ -133,7 +150,7 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts)
-- tty-test puts the terminal into raw mode and echoes input. Tests work by
-- feeding termcodes to control the display and asserting by screen:expect.
if command == default_command and screen_opts == nil then
if cmd == default_command and screen_opts == nil then
-- Wait for "tty ready" to be printed before each test or the terminal may
-- still be in canonical mode (will echo characters for example).
local empty_line = (' '):rep(cols)
@ -160,37 +177,24 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts)
return screen
end
local function setup_child_nvim(args, opts)
--- Spawns Nvim with `args` in a :terminal, and returns a `Screen` object.
---
--- @note Only use this if you actually need the full lifecycle/capabilities of the builtin Nvim
--- TUI. Most tests should just use `Screen.new()` directly, or plain old API calls.
---
---@param args? string[] Args passed to child Nvim.
---@param opts? table Options
---@return test.functional.ui.screen # Screen attached to the global (not child) Nvim session.
function M.setup_child_nvim(args, opts)
opts = opts or {}
local argv = { nvim_prog, unpack(args) }
local argv = { nvim_prog, unpack(args or {}) }
local env = opts.env or {}
if not env.VIMRUNTIME then
env.VIMRUNTIME = os.getenv('VIMRUNTIME')
end
return screen_setup(opts.extra_rows, argv, opts.cols, env)
return M.setup_screen(opts.extra_rows, argv, opts.cols, env)
end
return {
feed_data = feed_data,
feed_termcode = feed_termcode,
make_lua_executor = make_lua_executor,
hide_cursor = hide_cursor,
show_cursor = show_cursor,
enter_altscreen = enter_altscreen,
exit_altscreen = exit_altscreen,
set_fg = set_fg,
set_bg = set_bg,
set_bold = set_bold,
set_italic = set_italic,
set_underline = set_underline,
set_underdouble = set_underdouble,
set_undercurl = set_undercurl,
set_strikethrough = set_strikethrough,
clear_attrs = clear_attrs,
enable_mouse = enable_mouse,
disable_mouse = disable_mouse,
screen_setup = screen_setup,
setup_child_nvim = setup_child_nvim,
}
return M

View File

@ -8,6 +8,7 @@ local exec_lua = n.exec_lua
local pcall_err = t.pcall_err
local matches = t.matches
local insert = n.insert
local NIL = vim.NIL
before_each(clear)
@ -15,10 +16,12 @@ describe('treesitter language API', function()
-- error tests not requiring a parser library
it('handles missing language', function()
eq(
".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
'.../treesitter.lua:0: Parser not found.',
pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")
)
eq(NIL, exec_lua("return vim.treesitter._get_parser(0, 'borklang')"))
-- actual message depends on platform
matches(
"Failed to load parser for language 'borklang': uv_dlopen: .+",
@ -105,9 +108,10 @@ describe('treesitter language API', function()
command('set filetype=borklang')
-- Should throw an error when filetype changes to borklang
eq(
".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
'.../treesitter.lua:0: Parser not found.',
pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0, 'borklang')")
)
eq(NIL, exec_lua("return vim.treesitter._get_parser(0, 'borklang')"))
end
)

View File

@ -135,9 +135,7 @@ void ui_refresh(void)
insert(test_text)
eq(
'.../treesitter.lua:0: There is no parser available for buffer 1 and one'
.. ' could not be created because lang could not be determined. Either'
.. ' pass lang or set the buffer filetype',
'.../treesitter.lua:0: Parser not found.',
pcall_err(exec_lua, 'vim.treesitter.get_parser(0)')
)

View File

@ -1078,6 +1078,44 @@ describe('CursorLine and CursorLineNr highlights', function()
]])
end)
-- oldtest: Test_cursorline_screenline_resize()
it("'cursorlineopt' screenline is updated on window resize", function()
local screen = Screen.new(75, 8)
screen:attach()
exec([[
50vnew
call setline(1, repeat('xyz ', 30))
setlocal number cursorline cursorlineopt=screenline
normal! $
]])
screen:expect([[
{8: 1 }xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xy |
{8: }z xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz {1:~ }|
{8: }{21:xyz xyz xyz xyz xyz xyz xyz^ }{1:~ }|
{1:~ }{1:~ }|*3
{3:[No Name] [+] }{2:[No Name] }|
|
]])
command('vertical resize -4')
screen:expect([[
{8: 1 }xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xy |
{8: }z xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz {1:~ }|
{8: }{21:xyz xyz xyz xyz xyz xyz xyz xyz xyz^ }{1:~ }|
{1:~ }{1:~ }|*3
{3:[No Name] [+] }{2:[No Name] }|
|
]])
command('set cpoptions+=n')
screen:expect([[
{8: 1 }xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xy |
z xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz {1:~ }|
{21:xyz xyz xyz xyz xyz xyz xyz xyz^ }{1:~ }|
{1:~ }{1:~ }|*3
{3:[No Name] [+] }{2:[No Name] }|
|
]])
end)
-- oldtest: Test_cursorline_after_yank()
it('always updated. vim-patch:8.1.0849', function()
local screen = Screen.new(50, 5)

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local clear, insert = n.clear, n.insert
local command = n.command

View File

@ -1,7 +1,7 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.terminal.testutil')
local tt = require('test.functional.testterm')
local assert_alive = n.assert_alive
local mkdir, write_file, rmdir = t.mkdir, t.write_file, n.rmdir

View File

@ -262,14 +262,34 @@ func Test_cursorline_callback()
call timer_start(300, 'Func')
END
call writefile(lines, 'Xcul_timer')
call writefile(lines, 'Xcul_timer', 'D')
let buf = RunVimInTerminal('-S Xcul_timer', #{rows: 8})
call TermWait(buf, 310)
call VerifyScreenDump(buf, 'Test_cursorline_callback_1', {})
call StopVimInTerminal(buf)
call delete('Xcul_timer')
endfunc
func Test_cursorline_screenline_resize()
CheckScreendump
let lines =<< trim END
50vnew
call setline(1, repeat('xyz ', 30))
setlocal number cursorline cursorlineopt=screenline
normal! $
END
call writefile(lines, 'Xcul_screenline_resize', 'D')
let buf = RunVimInTerminal('-S Xcul_screenline_resize', #{rows: 8})
call VerifyScreenDump(buf, 'Test_cursorline_screenline_resize_1', {})
call term_sendkeys(buf, ":vertical resize -4\<CR>")
call VerifyScreenDump(buf, 'Test_cursorline_screenline_resize_2', {})
call term_sendkeys(buf, ":set cpoptions+=n\<CR>")
call VerifyScreenDump(buf, 'Test_cursorline_screenline_resize_3', {})
call StopVimInTerminal(buf)
endfunc
func Test_cursorline_screenline_update()
@ -280,7 +300,7 @@ func Test_cursorline_screenline_update()
set cursorline cursorlineopt=screenline
inoremap <F2> <Cmd>call cursor(1, 1)<CR>
END
call writefile(lines, 'Xcul_screenline')
call writefile(lines, 'Xcul_screenline', 'D')
let buf = RunVimInTerminal('-S Xcul_screenline', #{rows: 8})
call term_sendkeys(buf, "A")
@ -290,7 +310,17 @@ func Test_cursorline_screenline_update()
call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)
call delete('Xcul_screenline')
endfunc
func Test_cursorline_screenline_zero_width()
CheckOption foldcolumn
set cursorline culopt=screenline winminwidth=1 foldcolumn=1
" This used to crash Vim
1vnew | redraw
bwipe!
set cursorline& culopt& winminwidth& foldcolumn&
endfunc
func Test_cursorline_cursorbind_horizontal_scroll()

View File

@ -4071,11 +4071,23 @@ func Test_ll_window_ctx()
enew | only
endfunc
" Similar to the problem above, but for user data.
func Test_ll_window_user_data()
call setloclist(0, [#{bufnr: bufnr(), user_data: {}}])
lopen
wincmd t
close
call test_garbagecollect_now()
call feedkeys("\<CR>", 'tx')
call test_garbagecollect_now()
%bwipe!
endfunc
" The following test used to crash vim
func Test_lfile_crash()
sp Xtest
au QuickFixCmdPre * bw
call assert_fails('lfile', 'E40')
call assert_fails('lfile', 'E40:')
au! QuickFixCmdPre
endfunc