diff options
91 files changed, 1660 insertions, 1874 deletions
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index c3078cfd35..11f5f17bba 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -48,6 +48,7 @@ ee031eb5256bb83e0d6add2bae6fd943a4186ffe 69e11b58b4db0952f11a5ff85aa7150b5f5b8db8 271bb32855853b011fceaf0ad2f829bce66b2a19 aefdc6783cb77f09786542c90901a9e7120bea42 +aa4f9c5341f5280f16cce0630ea54b84eef717b3 # typos d238b8f6003d34cae7f65ff7585b48a2cd9449fb diff --git a/.stylua.toml b/.stylua.toml index 255bfd939d..a2b3447506 100644 --- a/.stylua.toml +++ b/.stylua.toml @@ -1,4 +1,4 @@ -column_width = 120 +column_width = 100 line_endings = "Unix" indent_type = "Spaces" indent_width = 2 diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 7f88216f43..7fff74a963 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -177,7 +177,9 @@ This means that the contents of compressed files are not inspected. If a file type that you want to use is not detected yet, there are a few ways to add it. In any way, it's better not to modify the $VIMRUNTIME/filetype.lua or $VIMRUNTIME/filetype.vim files. They will be overwritten when installing a -new version of Nvim. +new version of Nvim. The following explains the legacy Vim mechanism (enabled +if |do_legacy_filetype| is set). For Nvim's default mechanism, see +|vim.filetype.add()|. A. If you want to overrule all default file type checks. This works by writing one file for each filetype. The disadvantage is that @@ -236,39 +238,8 @@ C. If your file type can be detected by the file name or extension. Write this file as "filetype.vim" in your user runtime directory. For example, for Unix: > :w ~/.config/nvim/filetype.vim -< - Alternatively, create a file called "filetype.lua" that adds new - filetypes. - Example: > - vim.filetype.add({ - extension = { - foo = "fooscript", - }, - filename = { - [".foorc"] = "foorc", - }, - pattern = { - [".*/etc/foo/.*%.conf"] = "foorc", - }, - }) -< - See |vim.filetype.add()|. - *g:do_filetype_lua* - For now, Lua filetype detection is opt-in. You can enable it by adding - the following to your |init.vim|: > - let g:do_filetype_lua = 1 -< *g:did_load_filetypes* - In either case, the builtin filetype detection provided by Nvim can be - disabled by setting the did_load_filetypes global variable. If this - variable exists, $VIMRUNTIME/filetype.vim will not run. - Example: > - " Disable filetype.vim (but still load filetype.lua if enabled) - let g:did_load_filetypes = 0 - " Disable filetype.vim and filetype.lua - let g:did_load_filetypes = 1 - -< 3. To use the new filetype detection you must restart Vim. +< 3. To use the new filetype detection you must restart Vim. Your filetype.vim will be sourced before the default FileType autocommands have been installed. Your autocommands will match first, and the @@ -315,6 +286,16 @@ the 'runtimepath' for a directory to use. If there isn't one, set 'runtimepath' in the |system-vimrc|. Be careful to keep the default directories! + *g:do_legacy_filetype* +To disable Nvim's default filetype detection and revert to Vim's legacy +filetype detection, add the following to your |init.vim|: > + let g:do_legacy_filetype = 1 +< *g:did_load_filetypes* +The builtin filetype detection provided by Nvim can be disabled by setting +the `did_load_filetypes` global variable. If this variable exists, neither +the default `$VIMRUNTIME/filetype.lua` nor the legacy `$VIMRUNTIME/filetype.vim` +will run. + *plugin-details* The "plugin" directory can be in any of the directories in the 'runtimepath' option. All of these directories will be searched for plugins and they are diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index ee8424461e..c1d7f3a45a 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -2094,8 +2094,7 @@ start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params}) Lua module: vim.lsp.sync *lsp-sync* *vim.lsp.sync.compute_diff()* -compute_diff({prev_lines}, {curr_lines}, {firstline}, {lastline}, - {new_lastline}, {offset_encoding}, {line_ending}) +compute_diff({___MissingCloseParenHere___}) Returns the range table for the difference between prev and curr lines diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 3372bc90f7..089cf0ce9d 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2022,8 +2022,8 @@ add({filetypes}) *vim.filetype.add()* See $VIMRUNTIME/lua/vim/filetype.lua for more examples. - Note that Lua filetype detection is only enabled when - |g:do_filetype_lua| is set to 1. + Note that Lua filetype detection is disabled when + |g:do_legacy_filetype| is set. Example: > diff --git a/runtime/filetype.lua b/runtime/filetype.lua index f1885a8b95..9f5b5fd0dc 100644 --- a/runtime/filetype.lua +++ b/runtime/filetype.lua @@ -1,11 +1,8 @@ -if vim.g.did_load_filetypes and vim.g.did_load_filetypes ~= 0 then - return -end - --- For now, make this opt-in with a global variable -if vim.g.do_filetype_lua ~= 1 then +-- Skip if legacy filetype is enabled or filetype detection is disabled +if vim.g.do_legacy_filetype or vim.g.did_load_filetypes then return end +vim.g.did_load_filetypes = 1 vim.api.nvim_create_augroup('filetypedetect', { clear = false }) @@ -38,21 +35,16 @@ if not vim.g.did_load_ftdetect then ]]) end --- Set a marker so that the ftdetect scripts are not sourced a second time by filetype.vim -vim.g.did_load_ftdetect = 1 - --- If filetype.vim is disabled, set up the autocmd to use scripts.vim -if vim.g.did_load_filetypes then - vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, { - group = 'filetypedetect', - command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif", - }) +-- Set up the autocmd for user scripts.vim +vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, { + group = 'filetypedetect', + command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif", +}) - vim.api.nvim_create_autocmd('StdinReadPost', { - group = 'filetypedetect', - command = 'if !did_filetype() | runtime! scripts.vim | endif', - }) -end +vim.api.nvim_create_autocmd('StdinReadPost', { + group = 'filetypedetect', + command = 'if !did_filetype() | runtime! scripts.vim | endif', +}) if not vim.g.ft_ignore_pat then vim.g.ft_ignore_pat = '\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$' diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 44a3567983..7469f86cf5 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -3,6 +3,11 @@ " Maintainer: Bram Moolenaar <Bram@vim.org> " Last Change: 2022 Jul 5 +" Only run this if enabled +if !exists("do_legacy_filetype") + finish +endif + " Listen very carefully, I will say this only once if exists("did_load_filetypes") finish diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index f7dcc3a81b..7febad6ef6 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -739,7 +739,12 @@ function vim._cs_remote(rcid, server_addr, connect_error, args) f_tab = true elseif subcmd == 'silent' then f_silent = true - elseif subcmd == 'wait' or subcmd == 'wait-silent' or subcmd == 'tab-wait' or subcmd == 'tab-wait-silent' then + elseif + subcmd == 'wait' + or subcmd == 'wait-silent' + or subcmd == 'tab-wait' + or subcmd == 'tab-wait-silent' + then return { errmsg = 'E5600: Wait commands not yet implemented in nvim' } elseif subcmd == 'tab-silent' then f_tab = true @@ -795,7 +800,11 @@ function vim.deprecate(name, alternative, version, plugin, backtrace) local message = name .. ' is deprecated' plugin = plugin or 'Nvim' message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message - message = message .. ' See :h deprecated\nThis function will be removed in ' .. plugin .. ' version ' .. version + message = message + .. ' See :h deprecated\nThis function will be removed in ' + .. plugin + .. ' version ' + .. version if vim.notify_once(message, vim.log.levels.WARN) and backtrace ~= false then vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN) end diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 715a7e5561..f1652718ee 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -102,9 +102,13 @@ do -- buffer option accessor if type(k) == 'string' then _setup() if win_options[k] then - error(string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k)) + error( + string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k) + ) elseif glb_options[k] then - error(string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k)) + error( + string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k) + ) end end @@ -132,9 +136,13 @@ do -- window option accessor if type(k) == 'string' then _setup() if buf_options[k] then - error(string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k)) + error( + string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k) + ) elseif glb_options[k] then - error(string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k)) + error( + string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k) + ) end end @@ -252,7 +260,12 @@ local function assert_valid_value(name, value, types) end error( - string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, ' or ')) + string.format( + "Invalid option type '%s' for '%s', should be %s", + type_of_value, + name, + table.concat(types, ' or ') + ) ) end diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index afc0a4095c..40991673f3 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -68,7 +68,10 @@ local all_namespaces = {} ---@private local function to_severity(severity) if type(severity) == 'string' then - return assert(M.severity[string.upper(severity)], string.format('Invalid severity: %s', severity)) + return assert( + M.severity[string.upper(severity)], + string.format('Invalid severity: %s', severity) + ) end return severity end @@ -277,7 +280,8 @@ local function set_diagnostic_cache(namespace, bufnr, diagnostics) for _, diagnostic in ipairs(diagnostics) do assert(diagnostic.lnum, 'Diagnostic line number is required') assert(diagnostic.col, 'Diagnostic column is required') - diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity) or M.severity.ERROR + diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity) + or M.severity.ERROR diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum diagnostic.end_col = diagnostic.end_col or diagnostic.col diagnostic.namespace = namespace @@ -322,13 +326,8 @@ local function save_extmarks(namespace, bufnr) }) diagnostic_attached_buffers[bufnr] = true end - diagnostic_cache_extmarks[bufnr][namespace] = vim.api.nvim_buf_get_extmarks( - bufnr, - namespace, - 0, - -1, - { details = true } - ) + diagnostic_cache_extmarks[bufnr][namespace] = + vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true }) end local registered_autocmds = {} @@ -339,6 +338,32 @@ local function make_augroup_key(namespace, bufnr) return string.format('DiagnosticInsertLeave:%s:%s', bufnr, ns.name) end +---@private +local function execute_scheduled_display(namespace, bufnr) + local args = bufs_waiting_to_update[bufnr][namespace] + if not args then + return + end + + -- Clear the args so we don't display unnecessarily. + bufs_waiting_to_update[bufnr][namespace] = nil + + M.show(namespace, bufnr, nil, args) +end + +--- @deprecated +--- Callback scheduled when leaving Insert mode. +--- +--- called from the Vimscript autocommand. +--- +--- See @ref schedule_display() +--- +---@private +function M._execute_scheduled_display(namespace, bufnr) + vim.deprecate('vim.diagnostic._execute_scheduled_display', nil, '0.9') + execute_scheduled_display(namespace, bufnr) +end + --- Table of autocmd events to fire the update for displaying new diagnostic information local insert_leave_auto_cmds = { 'InsertLeave', 'CursorHoldI' } @@ -347,18 +372,15 @@ local function schedule_display(namespace, bufnr, args) bufs_waiting_to_update[bufnr][namespace] = args local key = make_augroup_key(namespace, bufnr) + local group = vim.api.nvim_create_augroup(key, { clear = true }) if not registered_autocmds[key] then - vim.cmd(string.format( - [[augroup %s - au! - autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s) - augroup END]], - key, - table.concat(insert_leave_auto_cmds, ','), - bufnr, - namespace, - bufnr - )) + vim.api.nvim_create_autocmd(insert_leave_auto_cmds, { + group = group, + buffer = bufnr, + callback = function() + execute_scheduled_display(namespace, bufnr) + end, + }) registered_autocmds[key] = true end end @@ -368,12 +390,7 @@ local function clear_scheduled_display(namespace, bufnr) local key = make_augroup_key(namespace, bufnr) if registered_autocmds[key] then - vim.cmd(string.format( - [[augroup %s - au! - augroup END]], - key - )) + vim.api.nvim_del_augroup_by_name(key) registered_autocmds[key] = nil end end @@ -482,7 +499,8 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace) bufnr = get_bufnr(bufnr) local wrap = vim.F.if_nil(opts.wrap, true) local line_count = vim.api.nvim_buf_line_count(bufnr) - local diagnostics = get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true) + local diagnostics = + get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true) local line_diagnostics = diagnostic_lines(diagnostics) for i = 0, line_count do local offset = i * (search_forward and 1 or -1) @@ -971,7 +989,10 @@ M.handlers.virtual_text = { if opts.virtual_text.format then diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics) end - if opts.virtual_text.source and (opts.virtual_text.source ~= 'if_many' or count_sources(bufnr) > 1) then + if + opts.virtual_text.source + and (opts.virtual_text.source ~= 'if_many' or count_sources(bufnr) > 1) + then diagnostics = prefix_source(diagnostics) end if opts.virtual_text.severity then @@ -1045,26 +1066,6 @@ function M._get_virt_text_chunks(line_diags, opts) end end ---- Callback scheduled when leaving Insert mode. ---- ---- This function must be exported publicly so that it is available to be ---- called from the Vimscript autocommand. ---- ---- See @ref schedule_display() ---- ----@private -function M._execute_scheduled_display(namespace, bufnr) - local args = bufs_waiting_to_update[bufnr][namespace] - if not args then - return - end - - -- Clear the args so we don't display unnecessarily. - bufs_waiting_to_update[bufnr][namespace] = nil - - M.show(namespace, bufnr, nil, args) -end - --- Hide currently displayed diagnostics. --- --- This only clears the decorations displayed in the buffer. Diagnostics can @@ -1279,7 +1280,9 @@ function M.open_float(opts, ...) -- LSP servers can send diagnostics with `end_col` past the length of the line local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1] diagnostics = vim.tbl_filter(function(d) - return d.lnum == lnum and math.min(d.col, line_length - 1) <= col and (d.end_col >= col or d.end_lnum > lnum) + return d.lnum == lnum + and math.min(d.col, line_length - 1) <= col + and (d.end_col >= col or d.end_lnum > lnum) end, diagnostics) end @@ -1333,9 +1336,10 @@ function M.open_float(opts, ...) diagnostics = prefix_source(diagnostics) end - local prefix_opt = if_nil(opts.prefix, (scope == 'cursor' and #diagnostics <= 1) and '' or function(_, i) - return string.format('%d. ', i) - end) + local prefix_opt = + if_nil(opts.prefix, (scope == 'cursor' and #diagnostics <= 1) and '' or function(_, i) + return string.format('%d. ', i) + end) local prefix, prefix_hl_group if prefix_opt then diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index df21fd46a1..8e86812347 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -176,7 +176,8 @@ local extension = { bsdl = 'bsdl', bst = 'bst', btm = function(path, bufnr) - return (vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0) and 'dosbatch' or 'btm' + return (vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0) and 'dosbatch' + or 'btm' end, bzl = 'bzl', bazel = 'bzl', @@ -2169,7 +2170,10 @@ local function sort_by_priority(t) local sorted = {} for k, v in pairs(t) do local ft = type(v) == 'table' and v[1] or v - assert(type(ft) == 'string' or type(ft) == 'function', 'Expected string or function for filetype') + assert( + type(ft) == 'string' or type(ft) == 'function', + 'Expected string or function for filetype' + ) local opts = (type(v) == 'table' and type(v[2]) == 'table') and v[2] or {} if not opts.priority then @@ -2223,8 +2227,7 @@ end --- --- See $VIMRUNTIME/lua/vim/filetype.lua for more examples. --- ---- Note that Lua filetype detection is only enabled when |g:do_filetype_lua| is ---- set to 1. +--- Note that Lua filetype detection is disabled when |g:do_legacy_filetype| is set. --- --- Example: --- <pre> diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index fb36a502b0..8331920406 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -71,7 +71,8 @@ function M.asm_syntax(bufnr) end end -local visual_basic_content = { 'vb_name', 'begin vb%.form', 'begin vb%.mdiform', 'begin vb%.usercontrol' } +local visual_basic_content = + { 'vb_name', 'begin vb%.form', 'begin vb%.mdiform', 'begin vb%.usercontrol' } -- See frm() for Visual Basic form file detection function M.bas(bufnr) @@ -92,7 +93,11 @@ function M.bas(bufnr) for _, line in ipairs(getlines(bufnr, 1, 100)) do if findany(line:lower(), visual_basic_content) then return 'vb' - elseif line:find(fb_comment) or matchregex(line, fb_preproc) or matchregex(line, fb_keywords) then + elseif + line:find(fb_comment) + or matchregex(line, fb_preproc) + or matchregex(line, fb_keywords) + then return 'freebasic' elseif matchregex(line, qb64_preproc) then return 'qb64' @@ -236,7 +241,13 @@ local function cvs_diff(path, contents) elseif -- Locale input files: Formal Definitions of Cultural Conventions -- Filename must be like en_US, fr_FR@euro or en_US.UTF-8 - findany(path, { '%a%a_%a%a$', '%a%a_%a%a[%.@]', '%a%a_%a%ai18n$', '%a%a_%a%aPOSIX$', '%a%a_%a%atranslit_' }) + findany(path, { + '%a%a_%a%a$', + '%a%a_%a%a[%.@]', + '%a%a_%a%ai18n$', + '%a%a_%a%aPOSIX$', + '%a%a_%a%atranslit_', + }) then -- Only look at the first 100 lines for line_nr = 1, 100 do @@ -327,9 +338,11 @@ local function diff(contents) contents[1]:find('^%-%-%- ') and contents[2]:find('^%+%+%+ ') or contents[1]:find('^%* looking for ') and contents[2]:find('^%* comparing to ') or contents[1]:find('^%*%*%* ') and contents[2]:find('^%-%-%- ') - or contents[1]:find('^=== ') and ((contents[2]:find('^' .. string.rep('=', 66)) and contents[3]:find('^%-%-% ') and contents[4]:find( - '^%+%+%+' - )) or (contents[2]:find('^%-%-%- ') and contents[3]:find('^%+%+%+ '))) + or contents[1]:find('^=== ') and ((contents[2]:find('^' .. string.rep('=', 66)) and contents[3]:find( + '^%-%-% ' + ) and contents[4]:find('^%+%+%+')) or (contents[2]:find('^%-%-%- ') and contents[3]:find( + '^%+%+%+ ' + ))) or findany(contents[1], { '^=== removed', '^=== added', '^=== renamed', '^=== modified' }) then return 'diff' @@ -523,7 +536,8 @@ function M.idl(bufnr) end local pascal_comments = { '^%s*{', '^%s*%(%*', '^%s*//' } -local pascal_keywords = [[\c^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>]] +local pascal_keywords = + [[\c^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>]] function M.inc(bufnr) if vim.g.filetype_inc then @@ -574,9 +588,19 @@ end -- (refactor of filetype.vim since the patterns are case-insensitive) function M.log(path) path = path:lower() - if findany(path, { 'upstream%.log', 'upstream%..*%.log', '.*%.upstream%.log', 'upstream%-.*%.log' }) then + if + findany( + path, + { 'upstream%.log', 'upstream%..*%.log', '.*%.upstream%.log', 'upstream%-.*%.log' } + ) + then return 'upstreamlog' - elseif findany(path, { 'upstreaminstall%.log', 'upstreaminstall%..*%.log', '.*%.upstreaminstall%.log' }) then + elseif + findany( + path, + { 'upstreaminstall%.log', 'upstreaminstall%..*%.log', '.*%.upstreaminstall%.log' } + ) + then return 'upstreaminstalllog' elseif findany(path, { 'usserver%.log', 'usserver%..*%.log', '.*%.usserver%.log' }) then return 'usserverlog' @@ -616,7 +640,8 @@ function M.m(bufnr) -- Excluding end(for|function|if|switch|while) common to Murphi local octave_block_terminators = [[\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>]] - local objc_preprocessor = [[\c^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>]] + local objc_preprocessor = + [[\c^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>]] -- Whether we've seen a multiline comment leader local saw_comment = false @@ -627,7 +652,11 @@ function M.m(bufnr) -- anything more definitive. saw_comment = true end - if line:find('^%s*//') or matchregex(line, [[\c^\s*@import\>]]) or matchregex(line, objc_preprocessor) then + if + line:find('^%s*//') + or matchregex(line, [[\c^\s*@import\>]]) + or matchregex(line, objc_preprocessor) + then return 'objc' end if @@ -869,7 +898,10 @@ function M.progress_cweb(bufnr) if vim.g.filetype_w then return vim.g.filetype_w else - if getlines(bufnr, 1):lower():find('^&analyze') or getlines(bufnr, 3):lower():find('^&global%-define') then + if + getlines(bufnr, 1):lower():find('^&analyze') + or getlines(bufnr, 3):lower():find('^&global%-define') + then return 'progress' else return 'cweb' @@ -914,10 +946,13 @@ end function M.psf(bufnr) local line = getlines(bufnr, 1):lower() if - findany( - line, - { '^%s*distribution%s*$', '^%s*installed_software%s*$', '^%s*root%s*$', '^%s*bundle%s*$', '^%s*product%s*$' } - ) + findany(line, { + '^%s*distribution%s*$', + '^%s*installed_software%s*$', + '^%s*root%s*$', + '^%s*bundle%s*$', + '^%s*product%s*$', + }) then return 'psf' end @@ -960,7 +995,9 @@ end function M.reg(bufnr) local line = getlines(bufnr, 1):lower() - if line:find('^regedit[0-9]*%s*$') or line:find('^windows registry editor version %d*%.%d*%s*$') then + if + line:find('^regedit[0-9]*%s*$') or line:find('^windows registry editor version %d*%.%d*%s*$') + then return 'registry' end end @@ -1033,10 +1070,15 @@ end function M.sc(bufnr) for _, line in ipairs(getlines(bufnr, 1, 25)) do if - findany( - line, - { '[A-Za-z0-9]*%s:%s[A-Za-z0-9]', 'var%s<', 'classvar%s<', '%^this.*', '|%w*|', '%+%s%w*%s{', '%*ar%s' } - ) + findany(line, { + '[A-Za-z0-9]*%s:%s[A-Za-z0-9]', + 'var%s<', + 'classvar%s<', + '%^this.*', + '|%w*|', + '%+%s%w*%s{', + '%*ar%s', + }) then return 'supercollider' end @@ -1062,10 +1104,11 @@ function M.sgml(bufnr) if lines:find('linuxdoc') then return 'smgllnx' elseif lines:find('<!DOCTYPE.*DocBook') then - return 'docbk', function(b) - vim.b[b].docbk_type = 'sgml' - vim.b[b].docbk_ver = 4 - end + return 'docbk', + function(b) + vim.b[b].docbk_type = 'sgml' + vim.b[b].docbk_ver = 4 + end else return 'sgml' end @@ -1194,7 +1237,8 @@ function M.tex(path, bufnr) if not l:find('^%s*%%%S') then -- Check the next thousand lines for a LaTeX or ConTeXt keyword. for _, line in ipairs(getlines(bufnr, i + 1, i + 1000)) do - local lpat_match, cpat_match = matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)\|^\s*\\\(]] .. cpat .. [[\)]]) + local lpat_match, cpat_match = + matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)\|^\s*\\\(]] .. cpat .. [[\)]]) if lpat_match then return 'tex' elseif cpat_match then @@ -1489,10 +1533,17 @@ local patterns_text = { -- Scheme scripts ['exec%s%+%S*scheme'] = { 'scheme', { start_lnum = 1, end_lnum = 2 } }, -- Git output - ['^\\(commit\\|tree\\|object\\) \\x\\{40,\\}\\>\\|^tag \\S\\+$'] = { 'git', { vim_regex = true } }, + ['^\\(commit\\|tree\\|object\\) \\x\\{40,\\}\\>\\|^tag \\S\\+$'] = { + 'git', + { vim_regex = true }, + }, function(lines) -- Gprof (gnu profiler) - if lines[1] == 'Flat profile:' and lines[2] == '' and lines[3]:find('^Each sample counts as .* seconds%.$') then + if + lines[1] == 'Flat profile:' + and lines[2] == '' + and lines[3]:find('^Each sample counts as .* seconds%.$') + then return 'gprof' end end, @@ -1515,7 +1566,12 @@ local function match_from_text(contents, path) if contents[1]:find('^:$') then -- Bourne-like shell scripts: sh ksh bash bash2 return M.sh(path, contents) - elseif matchregex('\n' .. table.concat(contents, '\n'), [[\n\s*emulate\s\+\%(-[LR]\s\+\)\=[ckz]\=sh\>]]) then + elseif + matchregex( + '\n' .. table.concat(contents, '\n'), + [[\n\s*emulate\s\+\%(-[LR]\s\+\)\=[ckz]\=sh\>]] + ) + then -- Z shell scripts return 'zsh' end @@ -1535,7 +1591,10 @@ local function match_from_text(contents, path) else local opts = type(v) == 'table' and v[2] or {} if opts.start_lnum and opts.end_lnum then - assert(not opts.ignore_case, 'ignore_case=true is ignored when start_lnum is also present, needs refactor') + assert( + not opts.ignore_case, + 'ignore_case=true is ignored when start_lnum is also present, needs refactor' + ) for i = opts.start_lnum, opts.end_lnum do if not contents[i] then break diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 6f9c48ca24..ce845eda15 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -61,7 +61,8 @@ end function M.dir(path) return function(fs) return vim.loop.fs_scandir_next(fs) - end, vim.loop.fs_scandir(M.normalize(path)) + end, + vim.loop.fs_scandir(M.normalize(path)) end --- Find files or directories in the given path. diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index 36e3c2ad20..ddd504a0e0 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -11,16 +11,25 @@ M.priorities = { ---@private function M.create(higroup, hi_info, default) + vim.deprecate('vim.highlight.create', 'vim.api.nvim_set_hl', '0.9') local options = {} -- TODO: Add validation for k, v in pairs(hi_info) do table.insert(options, string.format('%s=%s', k, v)) end - vim.cmd(string.format([[highlight %s %s %s]], default and 'default' or '', higroup, table.concat(options, ' '))) + vim.cmd( + string.format( + [[highlight %s %s %s]], + default and 'default' or '', + higroup, + table.concat(options, ' ') + ) + ) end ---@private function M.link(higroup, link_to, force) + vim.deprecate('vim.highlight.link', 'vim.api.nvim_set_hl', '0.9') vim.cmd(string.format([[highlight%s link %s %s]], force and '!' or ' default', higroup, link_to)) end diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index c19e55fb37..0a53fb203b 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -80,7 +80,13 @@ for i = 0, 31 do end local function escape(str) - return (gsub(gsub(gsub(str, '\\', '\\\\'), '(%c)%f[0-9]', longControlCharEscapes), '%c', shortControlCharEscapes)) + return ( + gsub( + gsub(gsub(str, '\\', '\\\\'), '(%c)%f[0-9]', longControlCharEscapes), + '%c', + shortControlCharEscapes + ) + ) end local function isIdentifier(str) @@ -181,11 +187,13 @@ local function processRecursive(process, item, path, visited) for k, v in rawpairs(processed) do processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) if processedKey ~= nil then - processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) + processedCopy[processedKey] = + processRecursive(process, v, makePath(path, processedKey), visited) end end - local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) + local mt = + processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) if type(mt) ~= 'table' then mt = nil end diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 8bf736a2ca..75c3b63da7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -200,7 +200,12 @@ local function validate_encoding(encoding) encoding = { encoding, 's' }, }) return valid_encodings[encoding:lower()] - or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding)) + or error( + string.format( + "Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", + encoding + ) + ) end ---@internal @@ -211,13 +216,15 @@ end ---@returns (string) the command ---@returns (list of strings) its arguments function lsp._cmd_parts(input) - vim.validate({ cmd = { - input, - function() - return vim.tbl_islist(input) - end, - 'list', - } }) + vim.validate({ + cmd = { + input, + function() + return vim.tbl_islist(input) + end, + 'list', + }, + }) local cmd = input[1] local cmd_args = {} @@ -274,7 +281,11 @@ local function validate_client_config(config) get_language_id = { config.get_language_id, 'f', true }, }) assert( - (not config.flags or not config.flags.debounce_text_changes or type(config.flags.debounce_text_changes) == 'number'), + ( + not config.flags + or not config.flags.debounce_text_changes + or type(config.flags.debounce_text_changes) == 'number' + ), 'flags.debounce_text_changes must be a number with the debounce time in milliseconds' ) @@ -474,7 +485,8 @@ do local uri = vim.uri_from_bufnr(bufnr) return function(client) if - vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change') == protocol.TextDocumentSyncKind.None + vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change') + == protocol.TextDocumentSyncKind.None then return end @@ -493,7 +505,8 @@ do if client.is_stopped() or not vim.api.nvim_buf_is_valid(bufnr) then return end - local changes = state.use_incremental_sync and buf_state.pending_changes or { full_changes() } + local changes = state.use_incremental_sync and buf_state.pending_changes + or { full_changes() } client.notify('textDocument/didChange', { textDocument = { uri = uri, @@ -852,7 +865,8 @@ end --- the client has been initialized. function lsp.start_client(config) local cleaned_config = validate_client_config(config) - local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding + local cmd, cmd_args, offset_encoding = + cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding config.flags = config.flags or {} config.settings = config.settings or {} @@ -921,7 +935,8 @@ function lsp.start_client(config) ---@see |vim.lsp.rpc.client_errors| for possible errors. Use ---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name. function dispatch.on_error(code, err) - local _ = log.error() and log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err }) + local _ = log.error() + and log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err }) err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) if config.on_error then local status, usererr = pcall(config.on_error, code, err) @@ -964,7 +979,8 @@ function lsp.start_client(config) changetracking.reset(client_id) if code ~= 0 or (signal ~= 0 and signal ~= 15) then - local msg = string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal) + local msg = + string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal) vim.schedule(function() vim.notify(msg, vim.log.levels.WARN) end) @@ -1082,7 +1098,8 @@ function lsp.start_client(config) -- These are the cleaned up capabilities we use for dynamically deciding -- when to send certain events to clients. - client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities") + client.server_capabilities = + assert(result.capabilities, "initialize result doesn't contain capabilities") client.server_capabilities = protocol.resolve_capabilities(client.server_capabilities) -- Deprecation wrapper: this will be removed in 0.8 @@ -1128,7 +1145,11 @@ function lsp.start_client(config) end end local _ = log.info() - and log.info(log_prefix, 'server_capabilities', { server_capabilities = client.server_capabilities }) + and log.info( + log_prefix, + 'server_capabilities', + { server_capabilities = client.server_capabilities } + ) -- Only assign after initialized. active_clients[client_id] = client @@ -1168,9 +1189,14 @@ function lsp.start_client(config) -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(client, bufnr) bufnr = resolve_bufnr(bufnr) - local _ = log.debug() and log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr) + local _ = log.debug() + and log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr) local success, request_id = rpc.request(method, params, function(err, result) - handler(err, result, { method = method, client_id = client_id, bufnr = bufnr, params = params }) + handler( + err, + result, + { method = method, client_id = client_id, bufnr = bufnr, params = params } + ) end, function(request_id) client.requests[request_id] = nil nvim_command('doautocmd <nomodeline> User LspRequest') @@ -1322,15 +1348,17 @@ end --- Notify all attached clients that a buffer has changed. local text_document_did_change_handler do - text_document_did_change_handler = function(_, bufnr, changedtick, firstline, lastline, new_lastline) - -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached - if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then - return true + text_document_did_change_handler = + function(_, bufnr, changedtick, firstline, lastline, new_lastline) + -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached + if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then + return true + end + util.buf_versions[bufnr] = changedtick + local compute_change_and_notify = + changetracking.prepare(bufnr, firstline, lastline, new_lastline) + for_each_buffer_client(bufnr, compute_change_and_notify) end - util.buf_versions[bufnr] = changedtick - local compute_change_and_notify = changetracking.prepare(bufnr, firstline, lastline, new_lastline) - for_each_buffer_client(bufnr, compute_change_and_notify) - end end -- Buffer lifecycle handler for textDocument/didSave @@ -1369,7 +1397,8 @@ function lsp.buf_attach_client(bufnr, client_id) }) bufnr = resolve_bufnr(bufnr) if not vim.api.nvim_buf_is_loaded(bufnr) then - local _ = log.warn() and log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr)) + local _ = log.warn() + and log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr)) return false end local buffer_client_ids = all_buffer_active_clients[bufnr] @@ -1447,7 +1476,13 @@ function lsp.buf_detach_client(bufnr, client_id) local client = lsp.get_client_by_id(client_id) if not client or not client.attached_buffers[bufnr] then - vim.notify(string.format('Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', client_id, bufnr)) + vim.notify( + string.format( + 'Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', + client_id, + bufnr + ) + ) return end @@ -1548,10 +1583,14 @@ function lsp.get_active_clients(filter) local clients = {} - local t = filter.bufnr and (all_buffer_active_clients[resolve_bufnr(filter.bufnr)] or {}) or active_clients + local t = filter.bufnr and (all_buffer_active_clients[resolve_bufnr(filter.bufnr)] or {}) + or active_clients for client_id in pairs(t) do local client = active_clients[client_id] - if (filter.id == nil or client.id == filter.id) and (filter.name == nil or client.name == filter.name) then + if + (filter.id == nil or client.id == filter.id) + and (filter.name == nil or client.name == filter.name) + then clients[#clients + 1] = client end end @@ -1643,7 +1682,9 @@ function lsp.buf_request(bufnr, method, params, handler) end) -- if has client but no clients support the given method, notify the user - if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then + if + not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported + then vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR) vim.api.nvim_command('redraw') return {}, function() end @@ -1888,12 +1929,17 @@ function lsp.formatexpr(opts) }, } params.options = util.make_formatting_params().options - local client_results = vim.lsp.buf_request_sync(0, 'textDocument/rangeFormatting', params, timeout_ms) + local client_results = + vim.lsp.buf_request_sync(0, 'textDocument/rangeFormatting', params, timeout_ms) -- Apply the text edits from one and only one of the clients. for client_id, response in pairs(client_results) do if response.result then - vim.lsp.util.apply_text_edits(response.result, 0, vim.lsp.get_client_by_id(client_id).offset_encoding) + vim.lsp.util.apply_text_edits( + response.result, + 0, + vim.lsp.get_client_by_id(client_id).offset_encoding + ) return 0 end end diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua index 910deba556..3488639fb4 100644 --- a/runtime/lua/vim/lsp/_snippet.lua +++ b/runtime/lua/vim/lsp/_snippet.lua @@ -255,7 +255,13 @@ S.format = P.any( S.int, S.colon, S.slash, - P.any(P.token('upcase'), P.token('downcase'), P.token('capitalize'), P.token('camelcase'), P.token('pascalcase')), + P.any( + P.token('upcase'), + P.token('downcase'), + P.token('capitalize'), + P.token('camelcase'), + P.token('pascalcase') + ), S.close ), function(values) @@ -272,7 +278,12 @@ S.format = P.any( S.open, S.int, S.colon, - P.seq(S.question, P.opt(P.take_until({ ':' }, { '\\' })), S.colon, P.opt(P.take_until({ '}' }, { '\\' }))), + P.seq( + S.question, + P.opt(P.take_until({ ':' }, { '\\' })), + S.colon, + P.opt(P.take_until({ '}' }, { '\\' })) + ), S.close ), function(values) @@ -285,7 +296,14 @@ S.format = P.any( end ), P.map( - P.seq(S.dollar, S.open, S.int, S.colon, P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))), S.close), + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))), + S.close + ), function(values) return setmetatable({ type = Node.Type.FORMAT, @@ -296,7 +314,15 @@ S.format = P.any( end ), P.map( - P.seq(S.dollar, S.open, S.int, S.colon, S.minus, P.opt(P.take_until({ '}' }, { '\\' })), S.close), + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + S.minus, + P.opt(P.take_until({ '}' }, { '\\' })), + S.close + ), function(values) return setmetatable({ type = Node.Type.FORMAT, @@ -306,14 +332,17 @@ S.format = P.any( }, Node) end ), - P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close), function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - if_text = '', - else_text = values[5] and values[5].esc or '', - }, Node) - end) + P.map( + P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close), + function(values) + return setmetatable({ + type = Node.Type.FORMAT, + capture_index = values[3], + if_text = '', + else_text = values[5] and values[5].esc or '', + }, Node) + end + ) ) S.transform = P.map( @@ -359,7 +388,14 @@ S.tabstop = P.any( S.placeholder = P.any( P.map( - P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))), S.close), + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))), + S.close + ), function(values) return setmetatable({ type = Node.Type.PLACEHOLDER, @@ -419,7 +455,14 @@ S.variable = P.any( }, Node) end), P.map( - P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), + P.seq( + S.dollar, + S.open, + S.var, + S.colon, + P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), + S.close + ), function(values) return setmetatable({ type = Node.Type.VARIABLE, diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 97c30bc46a..981aebada1 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -228,7 +228,8 @@ function M.format(options) end local params = util.make_formatting_params(options.formatting_options) client.request('textDocument/formatting', params, function(...) - local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting'] + local handler = client.handlers['textDocument/formatting'] + or vim.lsp.handlers['textDocument/formatting'] handler(...) do_format(next(clients, idx)) end, bufnr) @@ -284,7 +285,10 @@ end ---@param timeout_ms (number) Request timeout ---@see |vim.lsp.buf.formatting_seq_sync| function M.formatting_sync(options, timeout_ms) - vim.notify_once('vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN) + vim.notify_once( + 'vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', + vim.log.levels.WARN + ) local params = util.make_formatting_params(options) local bufnr = vim.api.nvim_get_current_buf() select_client('textDocument/formatting', function(client) @@ -318,7 +322,10 @@ end ---in the following order: first all clients that are not in the `order` list, then ---the remaining clients in the order as they occur in the `order` list. function M.formatting_seq_sync(options, timeout_ms, order) - vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN) + vim.notify_once( + 'vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', + vim.log.levels.WARN + ) local clients = vim.tbl_values(vim.lsp.buf_get_clients()) local bufnr = vim.api.nvim_get_current_buf() @@ -346,7 +353,10 @@ function M.formatting_seq_sync(options, timeout_ms, order) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then - vim.notify(string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), vim.log.levels.WARN) + vim.notify( + string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), + vim.log.levels.WARN + ) end end end @@ -429,7 +439,8 @@ function M.rename(new_name, options) local function rename(name) local params = util.make_position_params(win, client.offset_encoding) params.newName = name - local handler = client.handlers['textDocument/rename'] or vim.lsp.handlers['textDocument/rename'] + local handler = client.handlers['textDocument/rename'] + or vim.lsp.handlers['textDocument/rename'] client.request('textDocument/rename', params, function(...) handler(...) try_use_client(next(clients, idx)) @@ -443,7 +454,8 @@ function M.rename(new_name, options) if next(clients, idx) then try_use_client(next(clients, idx)) else - local msg = err and ('Error on prepareRename: ' .. (err.message or '')) or 'Nothing to rename' + local msg = err and ('Error on prepareRename: ' .. (err.message or '')) + or 'Nothing to rename' vim.notify(msg, vim.log.levels.INFO) end return @@ -475,7 +487,10 @@ function M.rename(new_name, options) end) end, bufnr) else - assert(client.supports_method('textDocument/rename'), 'Client must support textDocument/rename') + assert( + client.supports_method('textDocument/rename'), + 'Client must support textDocument/rename' + ) if new_name then rename(new_name) return @@ -587,7 +602,8 @@ end --- Add the folder at path to the workspace folders. If {path} is --- not provided, the user will be prompted for a path using |input()|. function M.add_workspace_folder(workspace_folder) - workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir') + workspace_folder = workspace_folder + or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir') vim.api.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return @@ -623,7 +639,8 @@ end --- {path} is not provided, the user will be prompted for --- a path using |input()|. function M.remove_workspace_folder(workspace_folder) - workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h')) + workspace_folder = workspace_folder + or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h')) vim.api.nvim_command('redraw') if not (workspace_folder and #workspace_folder > 0) then return diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 126be2a0ad..1f9d084e2b 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -22,17 +22,6 @@ local function get_client_id(client_id) end ---@private -local function get_bufnr(bufnr) - if not bufnr then - return vim.api.nvim_get_current_buf() - elseif bufnr == 0 then - return vim.api.nvim_get_current_buf() - end - - return bufnr -end - ----@private local function severity_lsp_to_vim(severity) if type(severity) == 'string' then severity = vim.lsp.protocol.DiagnosticSeverity[severity] @@ -238,73 +227,6 @@ function M.reset(client_id, buffer_client_map) end) end --- Deprecated Functions {{{ - ---- Save diagnostics to the current buffer. ---- ----@deprecated Prefer |vim.diagnostic.set()| ---- ---- Handles saving diagnostics from multiple clients in the same buffer. ----@param diagnostics Diagnostic[] ----@param bufnr number ----@param client_id number ----@private -function M.save(diagnostics, bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.save', 'vim.diagnostic.set', '0.8') - local namespace = M.get_namespace(client_id) - vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) -end --- }}} - ---- Get all diagnostics for clients ---- ----@deprecated Prefer |vim.diagnostic.get()| ---- ----@param client_id number Restrict included diagnostics to the client ---- If nil, diagnostics of all clients are included. ----@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[]) -function M.get_all(client_id) - vim.deprecate('vim.lsp.diagnostic.get_all', 'vim.diagnostic.get', '0.8') - local result = {} - local namespace - if client_id then - namespace = M.get_namespace(client_id) - end - for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do - local diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, { namespace = namespace })) - result[bufnr] = diagnostics - end - return result -end - ---- Return associated diagnostics for bufnr ---- ----@deprecated Prefer |vim.diagnostic.get()| ---- ----@param bufnr number ----@param client_id number|nil If nil, then return all of the diagnostics. ---- Else, return just the diagnostics associated with the client_id. ----@param predicate function|nil Optional function for filtering diagnostics -function M.get(bufnr, client_id, predicate) - vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8') - predicate = predicate or function() - return true - end - if client_id == nil then - local all_diagnostics = {} - vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _) - local iter_diagnostics = vim.tbl_filter(predicate, M.get(bufnr, iter_client_id)) - for _, diagnostic in ipairs(iter_diagnostics) do - table.insert(all_diagnostics, diagnostic) - end - end) - return all_diagnostics - end - - local namespace = M.get_namespace(client_id) - return diagnostic_vim_to_lsp(vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, { namespace = namespace }))) -end - --- Get the diagnostics by line --- --- Marked private as this is used internally by the LSP subsystem, but @@ -342,390 +264,4 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id) return diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, opts)) end ---- Get the counts for a particular severity ---- ----@deprecated Prefer |vim.diagnostic.get_count()| ---- ----@param bufnr number The buffer number ----@param severity DiagnosticSeverity ----@param client_id number the client id -function M.get_count(bufnr, severity, client_id) - vim.deprecate('vim.lsp.diagnostic.get_count', 'vim.diagnostic.get', '0.8') - severity = severity_lsp_to_vim(severity) - local opts = { severity = severity } - if client_id ~= nil then - opts.namespace = M.get_namespace(client_id) - end - - return #vim.diagnostic.get(bufnr, opts) -end - ---- Get the previous diagnostic closest to the cursor_position ---- ----@deprecated Prefer |vim.diagnostic.get_prev()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Previous diagnostic -function M.get_prev(opts) - vim.deprecate('vim.lsp.diagnostic.get_prev', 'vim.diagnostic.get_prev', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return diagnostic_vim_to_lsp({ vim.diagnostic.get_prev(opts) })[1] -end - ---- Return the pos, {row, col}, for the prev diagnostic in the current buffer. ---- ----@deprecated Prefer |vim.diagnostic.get_prev_pos()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Previous diagnostic position -function M.get_prev_pos(opts) - vim.deprecate('vim.lsp.diagnostic.get_prev_pos', 'vim.diagnostic.get_prev_pos', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.get_prev_pos(opts) -end - ---- Move to the previous diagnostic ---- ----@deprecated Prefer |vim.diagnostic.goto_prev()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| -function M.goto_prev(opts) - vim.deprecate('vim.lsp.diagnostic.goto_prev', 'vim.diagnostic.goto_prev', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.goto_prev(opts) -end - ---- Get the next diagnostic closest to the cursor_position ---- ----@deprecated Prefer |vim.diagnostic.get_next()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Next diagnostic -function M.get_next(opts) - vim.deprecate('vim.lsp.diagnostic.get_next', 'vim.diagnostic.get_next', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return diagnostic_vim_to_lsp({ vim.diagnostic.get_next(opts) })[1] -end - ---- Return the pos, {row, col}, for the next diagnostic in the current buffer. ---- ----@deprecated Prefer |vim.diagnostic.get_next_pos()| ---- ----@param opts table See |vim.lsp.diagnostic.goto_next()| ----@return table Next diagnostic position -function M.get_next_pos(opts) - vim.deprecate('vim.lsp.diagnostic.get_next_pos', 'vim.diagnostic.get_next_pos', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.get_next_pos(opts) -end - ---- Move to the next diagnostic ---- ----@deprecated Prefer |vim.diagnostic.goto_next()| -function M.goto_next(opts) - vim.deprecate('vim.lsp.diagnostic.goto_next', 'vim.diagnostic.goto_next', '0.8') - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - end - return vim.diagnostic.goto_next(opts) -end - ---- Set signs for given diagnostics ---- ----@deprecated Prefer |vim.diagnostic._set_signs()| ---- ----@param diagnostics Diagnostic[] ----@param bufnr number The buffer number ----@param client_id number the client id ----@param sign_ns number|nil ----@param opts table Configuration for signs. Keys: ---- - priority: Set the priority of the signs. ---- - severity_limit (DiagnosticSeverity): ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. -function M.set_signs(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_signs', nil, '0.8') - local namespace = M.get_namespace(client_id) - if opts and not opts.severity and opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - - vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) -end - ---- Set underline for given diagnostics ---- ----@deprecated Prefer |vim.diagnostic._set_underline()| ---- ----@param diagnostics Diagnostic[] ----@param bufnr number: The buffer number ----@param client_id number: The client id ----@param diagnostic_ns number|nil: The namespace ----@param opts table: Configuration table: ---- - severity_limit (DiagnosticSeverity): ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. -function M.set_underline(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_underline', nil, '0.8') - local namespace = M.get_namespace(client_id) - if opts and not opts.severity and opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - return vim.diagnostic._set_underline(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) -end - ---- Set virtual text given diagnostics ---- ----@deprecated Prefer |vim.diagnostic._set_virtual_text()| ---- ----@param diagnostics Diagnostic[] ----@param bufnr number ----@param client_id number ----@param diagnostic_ns number ----@param opts table Options on how to display virtual text. Keys: ---- - prefix (string): Prefix to display before virtual text on line ---- - spacing (number): Number of spaces to insert before virtual text ---- - severity_limit (DiagnosticSeverity): ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. -function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) - vim.deprecate('vim.lsp.diagnostic.set_virtual_text', nil, '0.8') - local namespace = M.get_namespace(client_id) - if opts and not opts.severity and opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - return vim.diagnostic._set_virtual_text(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) -end - ---- Default function to get text chunks to display using |nvim_buf_set_extmark()|. ---- ----@deprecated Prefer |vim.diagnostic.get_virt_text_chunks()| ---- ----@param bufnr number The buffer to display the virtual text in ----@param line number The line number to display the virtual text on ----@param line_diags Diagnostic[] The diagnostics associated with the line ----@param opts table See {opts} from |vim.lsp.diagnostic.set_virtual_text()| ----@return an array of [text, hl_group] arrays. This can be passed directly to ---- the {virt_text} option of |nvim_buf_set_extmark()|. -function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts) - vim.deprecate('vim.lsp.diagnostic.get_virtual_text_chunks_for_line', nil, '0.8') - return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts) -end - ---- Open a floating window with the diagnostics from {position} ---- ----@deprecated Prefer |vim.diagnostic.show_position_diagnostics()| ---- ----@param opts table|nil Configuration keys ---- - severity: (DiagnosticSeverity, default nil) ---- - Only return diagnostics with this severity. Overrides severity_limit ---- - severity_limit: (DiagnosticSeverity, default nil) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - all opts for |show_diagnostics()| can be used here ----@param buf_nr number|nil The buffer number ----@param position table|nil The (0,0)-indexed position ----@return table {popup_bufnr, win_id} -function M.show_position_diagnostics(opts, buf_nr, position) - vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8') - opts = opts or {} - opts.scope = 'cursor' - opts.pos = position - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - return vim.diagnostic.open_float(buf_nr, opts) -end - ---- Open a floating window with the diagnostics from {line_nr} ---- ----@deprecated Prefer |vim.diagnostic.open_float()| ---- ----@param opts table Configuration table ---- - all opts for |vim.lsp.diagnostic.get_line_diagnostics()| and ---- |show_diagnostics()| can be used here ----@param buf_nr number|nil The buffer number ----@param line_nr number|nil The line number ----@param client_id number|nil the client id ----@return table {popup_bufnr, win_id} -function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) - vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8') - opts = opts or {} - opts.scope = 'line' - opts.pos = line_nr - if client_id then - opts.namespace = M.get_namespace(client_id) - end - return vim.diagnostic.open_float(buf_nr, opts) -end - ---- Redraw diagnostics for the given buffer and client ---- ----@deprecated Prefer |vim.diagnostic.show()| ---- ---- This calls the "textDocument/publishDiagnostics" handler manually using ---- the cached diagnostics already received from the server. This can be useful ---- for redrawing diagnostics after making changes in diagnostics ---- configuration. |lsp-handler-configuration| ---- ----@param bufnr (optional, number): Buffer handle, defaults to current ----@param client_id (optional, number): Redraw diagnostics for the given ---- client. The default is to redraw diagnostics for all attached ---- clients. -function M.redraw(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.redraw', 'vim.diagnostic.show', '0.8') - bufnr = get_bufnr(bufnr) - if not client_id then - return vim.lsp.for_each_buffer_client(bufnr, function(client) - M.redraw(bufnr, client.id) - end) - end - - local namespace = M.get_namespace(client_id) - return vim.diagnostic.show(namespace, bufnr) -end - ---- Sets the quickfix list ---- ----@deprecated Prefer |vim.diagnostic.setqflist()| ---- ----@param opts table|nil Configuration table. Keys: ---- - {open}: (boolean, default true) ---- - Open quickfix list after set ---- - {client_id}: (number) ---- - If nil, will consider all clients attached to buffer. ---- - {severity}: (DiagnosticSeverity) ---- - Exclusive severity to consider. Overrides {severity_limit} ---- - {severity_limit}: (DiagnosticSeverity) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - {workspace}: (boolean, default true) ---- - Set the list with workspace diagnostics -function M.set_qflist(opts) - vim.deprecate('vim.lsp.diagnostic.set_qflist', 'vim.diagnostic.setqflist', '0.8') - opts = opts or {} - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - if opts.client_id then - opts.client_id = nil - opts.namespace = M.get_namespace(opts.client_id) - end - local workspace = vim.F.if_nil(opts.workspace, true) - opts.bufnr = not workspace and 0 - return vim.diagnostic.setqflist(opts) -end - ---- Sets the location list ---- ----@deprecated Prefer |vim.diagnostic.setloclist()| ---- ----@param opts table|nil Configuration table. Keys: ---- - {open}: (boolean, default true) ---- - Open loclist after set ---- - {client_id}: (number) ---- - If nil, will consider all clients attached to buffer. ---- - {severity}: (DiagnosticSeverity) ---- - Exclusive severity to consider. Overrides {severity_limit} ---- - {severity_limit}: (DiagnosticSeverity) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - {workspace}: (boolean, default false) ---- - Set the list with workspace diagnostics -function M.set_loclist(opts) - vim.deprecate('vim.lsp.diagnostic.set_loclist', 'vim.diagnostic.setloclist', '0.8') - opts = opts or {} - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) } - end - if opts.client_id then - opts.client_id = nil - opts.namespace = M.get_namespace(opts.client_id) - end - local workspace = vim.F.if_nil(opts.workspace, false) - opts.bufnr = not workspace and 0 - return vim.diagnostic.setloclist(opts) -end - ---- Disable diagnostics for the given buffer and client ---- ----@deprecated Prefer |vim.diagnostic.disable()| ---- ----@param bufnr (optional, number): Buffer handle, defaults to current ----@param client_id (optional, number): Disable diagnostics for the given ---- client. The default is to disable diagnostics for all attached ---- clients. --- Note that when diagnostics are disabled for a buffer, the server will still --- send diagnostic information and the client will still process it. The --- diagnostics are simply not displayed to the user. -function M.disable(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.disable', 'vim.diagnostic.disable', '0.8') - if not client_id then - return vim.lsp.for_each_buffer_client(bufnr, function(client) - M.disable(bufnr, client.id) - end) - end - - bufnr = get_bufnr(bufnr) - local namespace = M.get_namespace(client_id) - return vim.diagnostic.disable(bufnr, namespace) -end - ---- Enable diagnostics for the given buffer and client ---- ----@deprecated Prefer |vim.diagnostic.enable()| ---- ----@param bufnr (optional, number): Buffer handle, defaults to current ----@param client_id (optional, number): Enable diagnostics for the given ---- client. The default is to enable diagnostics for all attached ---- clients. -function M.enable(bufnr, client_id) - vim.deprecate('vim.lsp.diagnostic.enable', 'vim.diagnostic.enable', '0.8') - if not client_id then - return vim.lsp.for_each_buffer_client(bufnr, function(client) - M.enable(bufnr, client.id) - end) - end - - bufnr = get_bufnr(bufnr) - local namespace = M.get_namespace(client_id) - return vim.diagnostic.enable(bufnr, namespace) -end - --- }}} - return M diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 935f4b64f8..8a64e64396 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -125,7 +125,8 @@ M['workspace/applyEdit'] = function(_, workspace_edit, ctx) if workspace_edit.label then print('Workspace edit', workspace_edit.label) end - local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) + local status, result = + pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) return { applied = status, failureReason = result, @@ -137,7 +138,11 @@ M['workspace/configuration'] = function(_, result, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then - err_message('LSP[', client_id, '] client has shut down after sending a workspace/configuration request') + err_message( + 'LSP[', + client_id, + '] client has shut down after sending a workspace/configuration request' + ) return end if not result.items then @@ -239,10 +244,14 @@ local function response_to_list(map_result, entity, title_fn) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx) - local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.') - return string.format('Symbols in %s', fname) -end) +M['textDocument/documentSymbol'] = response_to_list( + util.symbols_to_items, + 'document symbols', + function(ctx) + local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.') + return string.format('Symbols in %s', fname) + end +) --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols', function(ctx) @@ -391,7 +400,8 @@ function M.signature_help(_, result, ctx, config) return end local client = vim.lsp.get_client_by_id(ctx.client_id) - local triggers = vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') + local triggers = + vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype') local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) lines = util.trim_empty_lines(lines) diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index bf8fe0932e..ba730e3d6d 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -11,7 +11,12 @@ function M.check() report_info(string.format('LSP log level : %s', log_level_string)) if current_log_level < log.levels.WARN then - report_warn(string.format('Log level %s will cause degraded performance and high disk usage', log_level_string)) + report_warn( + string.format( + 'Log level %s will cause degraded performance and high disk usage', + log_level_string + ) + ) end local log_path = vim.lsp.get_log_path() diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 29cb27d373..6c6ba0f206 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -59,7 +59,11 @@ do local log_info = vim.loop.fs_stat(logfilename) if log_info and log_info.size > 1e9 then - local warn_msg = string.format('LSP client log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename) + local warn_msg = string.format( + 'LSP client log is large (%d MB): %s', + log_info.size / (1000 * 1000), + logfilename + ) vim.notify(warn_msg) end @@ -129,7 +133,8 @@ vim.tbl_add_reverse_lookup(log.levels) ---@param level (string or number) One of `vim.lsp.log.levels` function log.set_level(level) if type(level) == 'string' then - current_log_level = assert(log.levels[level:upper()], string.format('Invalid log level: %q', level)) + current_log_level = + assert(log.levels[level:upper()], string.format('Invalid log level: %q', level)) else assert(type(level) == 'number', 'level must be a number or string') assert(log.levels[level], string.format('Invalid log level: %d', level)) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 6ecf7891c7..6ecb9959d5 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -880,7 +880,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) general_properties.document_symbol = server_capabilities.documentSymbolProvider or false general_properties.workspace_symbol = server_capabilities.workspaceSymbolProvider or false general_properties.document_formatting = server_capabilities.documentFormattingProvider or false - general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false + general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider + or false general_properties.call_hierarchy = server_capabilities.callHierarchyProvider or false general_properties.execute_command = server_capabilities.executeCommandProvider ~= nil @@ -897,7 +898,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) general_properties.code_lens_resolve = false elseif type(server_capabilities.codeLensProvider) == 'table' then general_properties.code_lens = true - general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false + general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider + or false else error('The server sent invalid codeLensProvider') end @@ -974,7 +976,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) signature_help_properties = { signature_help = true, -- The characters that trigger signature help automatically. - signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {}, + signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters + or {}, } else error('The server sent invalid signatureHelpProvider') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index ad2498fb6f..cf74dd2b47 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -115,7 +115,8 @@ local function request_parser_loop() local body_length = #body_chunks[1] -- Keep waiting for data until we have enough. while body_length < content_length do - local chunk = coroutine.yield() or error('Expected more data for the body. The server may have died.') -- TODO hmm. + local chunk = coroutine.yield() + or error('Expected more data for the body. The server may have died.') -- TODO hmm. table.insert(body_chunks, chunk) body_length = body_length + #chunk end @@ -129,10 +130,16 @@ local function request_parser_loop() local body = table.concat(body_chunks) -- Yield our data. buffer = rest - .. (coroutine.yield(headers, body) or error('Expected more data for the body. The server may have died.')) -- TODO hmm. + .. ( + coroutine.yield(headers, body) + or error('Expected more data for the body. The server may have died.') + ) -- TODO hmm. else -- Get more data since we don't have enough. - buffer = buffer .. (coroutine.yield() or error('Expected more data for the header. The server may have died.')) -- TODO hmm. + buffer = buffer + .. ( + coroutine.yield() or error('Expected more data for the header. The server may have died.') + ) -- TODO hmm. end end end @@ -262,7 +269,8 @@ end --- - {handle} A handle for low-level interaction with the LSP server process --- |vim.loop|. local function start(cmd, cmd_args, dispatchers, extra_spawn_params) - local _ = log.info() and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) + local _ = log.info() + and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) validate({ cmd = { cmd, 's' }, cmd_args = { cmd_args, 't' }, @@ -336,7 +344,8 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) if handle == nil then local msg = string.format('Spawning language server with cmd: `%s` failed', cmd) if string.match(pid, 'ENOENT') then - msg = msg .. '. The language server is either not installed, missing from PATH, or not executable.' + msg = msg + .. '. The language server is either not installed, missing from PATH, or not executable.' else msg = msg .. string.format(' with error message: %s', pid) end @@ -476,7 +485,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) decoded.params ) local _ = log.debug() - and log.debug('server_request: callback result', { status = status, result = result, err = err }) + and log.debug( + 'server_request: callback result', + { status = status, result = result, err = err } + ) if status then if not (result or err) then -- TODO this can be a problem if `null` is sent for result. needs vim.NIL @@ -488,7 +500,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ) end if err then - assert(type(err) == 'table', 'err must be a table. Use rpc_response_error to help format errors.') + assert( + type(err) == 'table', + 'err must be a table. Use rpc_response_error to help format errors.' + ) local code_name = assert( protocol.ErrorCodes[err.code], 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.' @@ -549,14 +564,25 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) __tostring = format_rpc_error, }) end - try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, callback, decoded.error, decoded.result) + try_call( + client_errors.SERVER_RESULT_CALLBACK_ERROR, + callback, + decoded.error, + decoded.result + ) else on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) - local _ = log.error() and log.error('No callback found for server response id ' .. result_id) + local _ = log.error() + and log.error('No callback found for server response id ' .. result_id) end elseif type(decoded.method) == 'string' then -- Notification - try_call(client_errors.NOTIFICATION_HANDLER_ERROR, dispatchers.notification, decoded.method, decoded.params) + try_call( + client_errors.NOTIFICATION_HANDLER_ERROR, + dispatchers.notification, + decoded.method, + decoded.params + ) else -- Invalid server message on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 73b4e0025a..0d65e86b55 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -130,7 +130,14 @@ end ---@param new_lastline integer new_lastline from on_lines, adjusted to 1-index ---@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) ---@returns table<int, int> line_idx, byte_idx, and char_idx of first change position -local function compute_start_range(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding) +local function compute_start_range( + prev_lines, + curr_lines, + firstline, + lastline, + new_lastline, + offset_encoding +) local char_idx local byte_idx -- If firstline == lastline, no existing text is changed. All edit operations @@ -249,13 +256,19 @@ local function compute_end_range( local max_length if start_line_idx == prev_line_idx then -- Search until beginning of difference - max_length = min(prev_line_length - start_range.byte_idx, curr_line_length - start_range.byte_idx) + 1 + max_length = min( + prev_line_length - start_range.byte_idx, + curr_line_length - start_range.byte_idx + ) + 1 else max_length = min(prev_line_length, curr_line_length) + 1 end for idx = 0, max_length do byte_offset = idx - if str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) then + if + str_byte(prev_line, prev_line_length - byte_offset) + ~= str_byte(curr_line, curr_line_length - byte_offset) + then break end end @@ -268,8 +281,10 @@ local function compute_end_range( if prev_end_byte_idx == 0 then prev_end_byte_idx = 1 end - local prev_byte_idx, prev_char_idx = align_end_position(prev_line, prev_end_byte_idx, offset_encoding) - local prev_end_range = { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx } + local prev_byte_idx, prev_char_idx = + align_end_position(prev_line, prev_end_byte_idx, offset_encoding) + local prev_end_range = + { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx } local curr_end_range -- Deletion event, new_range cannot be before start @@ -281,8 +296,10 @@ local function compute_end_range( if curr_end_byte_idx == 0 then curr_end_byte_idx = 1 end - local curr_byte_idx, curr_char_idx = align_end_position(curr_line, curr_end_byte_idx, offset_encoding) - curr_end_range = { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx } + local curr_byte_idx, curr_char_idx = + align_end_position(curr_line, curr_end_byte_idx, offset_encoding) + curr_end_range = + { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx } end return prev_end_range, curr_end_range @@ -341,7 +358,10 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi local start_line = lines[start_range.line_idx] local range_length if start_line and #start_line > 0 then - range_length = compute_line_length(start_line, offset_encoding) - start_range.char_idx + 1 + line_ending_length + range_length = compute_line_length(start_line, offset_encoding) + - start_range.char_idx + + 1 + + line_ending_length else -- Length of newline character range_length = line_ending_length @@ -373,7 +393,15 @@ end ---@param new_lastline number line to begin search in new_lines for last difference ---@param offset_encoding string encoding requested by language server ---@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent -function M.compute_diff(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding, line_ending) +function M.compute_diff( + prev_lines, + curr_lines, + firstline, + lastline, + new_lastline, + offset_encoding, + line_ending +) -- Find the start of changes between the previous and current buffer. Common between both. -- Sent to the server as the start of the changed range. -- Used to grab the changed text from the latest buffer. @@ -403,7 +431,8 @@ function M.compute_diff(prev_lines, curr_lines, firstline, lastline, new_lastlin local text = extract_text(curr_lines, start_range, curr_end_range, line_ending) -- Compute the range of the replaced text. Deprecated but still required for certain language servers - local range_length = compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending) + local range_length = + compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending) -- convert to 0 based indexing local result = { diff --git a/runtime/lua/vim/lsp/tagfunc.lua b/runtime/lua/vim/lsp/tagfunc.lua index 5c55e8559f..f0ae6a6c49 100644 --- a/runtime/lua/vim/lsp/tagfunc.lua +++ b/runtime/lua/vim/lsp/tagfunc.lua @@ -44,7 +44,8 @@ end ---@private local function query_workspace_symbols(pattern) - local results_by_client, err = lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000) + local results_by_client, err = + lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000) if err then return {} end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b041385c9c..73476c0795 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -44,12 +44,22 @@ local function get_border_size(opts) shadow = { 1, 1 }, } if border_size[border] == nil then - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end height, width = unpack(border_size[border]) else if 8 % #border ~= 0 then - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end ---@private local function border_width(id) @@ -61,7 +71,12 @@ local function get_border_size(opts) -- border specified as a list of border characters return vim.fn.strdisplaywidth(border[id]) end - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end ---@private local function border_height(id) @@ -73,7 +88,12 @@ local function get_border_size(opts) -- border specified as a list of border characters return #border[id] > 0 and 1 or 0 end - error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border))) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ) + ) end height = height + border_height(2) -- top height = height + border_height(6) -- bottom @@ -531,7 +551,10 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) if offset_encoding == nil then - vim.notify_once('apply_text_document_edit must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'apply_text_document_edit must be called with valid offset encoding', + vim.log.levels.WARN + ) end -- For lists of text document edits, @@ -765,7 +788,10 @@ end --see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit function M.apply_workspace_edit(workspace_edit, offset_encoding) if offset_encoding == nil then - vim.notify_once('apply_workspace_edit must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'apply_workspace_edit must be called with valid offset encoding', + vim.log.levels.WARN + ) end if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do @@ -1022,7 +1048,10 @@ function M.jump_to_location(location, offset_encoding, reuse_win) return end if offset_encoding == nil then - vim.notify_once('jump_to_location must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'jump_to_location must be called with valid offset encoding', + vim.log.levels.WARN + ) end local bufnr = vim.uri_to_bufnr(uri) -- Save position in jumplist @@ -1226,14 +1255,21 @@ function M.stylize_markdown(bufnr, contents, opts) -- strip any empty lines or separators prior to this separator in actual markdown if line:match('^---+$') then while - markdown_lines[#stripped] and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) + markdown_lines[#stripped] + and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) do markdown_lines[#stripped] = false table.remove(stripped, #stripped) end end -- add the line if its not an empty line following a separator - if not (line:match('^%s*$') and markdown_lines[#stripped] and stripped[#stripped]:match('^---+$')) then + if + not ( + line:match('^%s*$') + and markdown_lines[#stripped] + and stripped[#stripped]:match('^---+$') + ) + then table.insert(stripped, line) markdown_lines[#stripped] = true end @@ -1265,7 +1301,11 @@ function M.stylize_markdown(bufnr, contents, opts) local function apply_syntax_to_region(ft, start, finish) if ft == '' then vim.cmd( - string.format('syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend', start, finish + 1) + string.format( + 'syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend', + start, + finish + 1 + ) ) return end @@ -1283,7 +1323,13 @@ function M.stylize_markdown(bufnr, contents, opts) langs[lang] = true end vim.cmd( - string.format('syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend', name, start, finish + 1, lang) + string.format( + 'syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend', + name, + start, + finish + 1, + lang + ) ) end @@ -1309,49 +1355,11 @@ function M.stylize_markdown(bufnr, contents, opts) end ---@private ---- Creates autocommands to close a preview window when events happen. ---- ----@param events table list of events ----@param winnr number window id of preview window ----@param bufnrs table list of buffers where the preview window will remain visible ----@see |autocmd-events| -local function close_preview_autocmd(events, winnr, bufnrs) - local augroup = 'preview_window_' .. winnr - - -- close the preview window when entered a buffer that is not - -- the floating window buffer or the buffer that spawned it - vim.cmd(string.format( - [[ - augroup %s - autocmd! - autocmd BufEnter * lua vim.lsp.util._close_preview_window(%d, {%s}) - augroup end - ]], - augroup, - winnr, - table.concat(bufnrs, ',') - )) - - if #events > 0 then - vim.cmd(string.format( - [[ - augroup %s - autocmd %s <buffer> lua vim.lsp.util._close_preview_window(%d) - augroup end - ]], - augroup, - table.concat(events, ','), - winnr - )) - end -end - ----@private --- Closes the preview window --- ---@param winnr number window id of preview window ---@param bufnrs table|nil optional list of ignored buffers -function M._close_preview_window(winnr, bufnrs) +local function close_preview_window(winnr, bufnrs) vim.schedule(function() -- exit if we are in one of ignored buffers if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then @@ -1359,20 +1367,42 @@ function M._close_preview_window(winnr, bufnrs) end local augroup = 'preview_window_' .. winnr - vim.cmd(string.format( - [[ - augroup %s - autocmd! - augroup end - augroup! %s - ]], - augroup, - augroup - )) + api.nvim_del_augroup_by_name(augroup) pcall(vim.api.nvim_win_close, winnr, true) end) end +---@private +--- Creates autocommands to close a preview window when events happen. +--- +---@param events table list of events +---@param winnr number window id of preview window +---@param bufnrs table list of buffers where the preview window will remain visible +---@see |autocmd-events| +local function close_preview_autocmd(events, winnr, bufnrs) + local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { + clear = true, + }) + + -- close the preview window when entered a buffer that is not + -- the floating window buffer or the buffer that spawned it + api.nvim_create_autocmd('BufEnter', { + group = augroup, + callback = function() + close_preview_window(winnr, bufnrs) + end, + }) + + if #events > 0 then + api.nvim_create_autocmd(events, { + buffer = bufnrs[2], + callback = function() + close_preview_window(winnr) + end, + }) + end +end + ---@internal --- Computes size of float needed to show contents (with optional wrapping) --- @@ -1587,15 +1617,21 @@ do --[[ References ]] offset_encoding = { offset_encoding, 'string', false }, }) for _, reference in ipairs(references) do - local start_line, start_char = reference['range']['start']['line'], reference['range']['start']['character'] - local end_line, end_char = reference['range']['end']['line'], reference['range']['end']['character'] + local start_line, start_char = + reference['range']['start']['line'], reference['range']['start']['character'] + local end_line, end_char = + reference['range']['end']['line'], reference['range']['end']['character'] local start_idx = get_line_byte_from_position( bufnr, { line = start_line, character = start_char }, offset_encoding ) - local end_idx = get_line_byte_from_position(bufnr, { line = start_line, character = end_char }, offset_encoding) + local end_idx = get_line_byte_from_position( + bufnr, + { line = start_line, character = end_char }, + offset_encoding + ) local document_highlight_kind = { [protocol.DocumentHighlightKind.Text] = 'LspReferenceText', @@ -1630,7 +1666,10 @@ end) ---@returns (table) list of items function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then - vim.notify_once('locations_to_items must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'locations_to_items must be called with valid offset encoding', + vim.log.levels.WARN + ) end local items = {} @@ -1683,35 +1722,6 @@ function M.locations_to_items(locations, offset_encoding) return items end ---- Fills target window's location list with given list of items. ---- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. ---- Defaults to current window. ---- ----@deprecated Use |setloclist()| ---- ----@param items (table) list of items -function M.set_loclist(items, win_id) - vim.deprecate('vim.lsp.util.set_loclist', 'setloclist', '0.8') - vim.fn.setloclist(win_id or 0, {}, ' ', { - title = 'Language Server', - items = items, - }) -end - ---- Fills quickfix list with given list of items. ---- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. ---- ----@deprecated Use |setqflist()| ---- ----@param items (table) list of items -function M.set_qflist(items) - vim.deprecate('vim.lsp.util.set_qflist', 'setqflist', '0.8') - vim.fn.setqflist({}, ' ', { - title = 'Language Server', - items = items, - }) -end - -- According to LSP spec, if the client set "symbolKind.valueSet", -- the client must handle it properly even if it receives a value outside the specification. -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol @@ -1857,7 +1867,10 @@ function M._get_offset_encoding(bufnr) for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do if client.offset_encoding == nil then vim.notify_once( - string.format('Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.', client.id), + string.format( + 'Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.', + client.id + ), vim.log.levels.ERROR ) end @@ -1994,7 +2007,10 @@ end function M.character_offset(buf, row, col, offset_encoding) local line = get_line(buf, row) if offset_encoding == nil then - vim.notify_once('character_offset must be called with valid offset encoding', vim.log.levels.WARN) + vim.notify_once( + 'character_offset must be called with valid offset encoding', + vim.log.levels.WARN + ) end -- If the col is past the EOL, use the line length. if col > #line then diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 9bbe356f03..d6c3e25b3b 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -254,7 +254,11 @@ local function tbl_extend(behavior, deep_extend, ...) end if select('#', ...) < 2 then - error('wrong number of arguments (given ' .. tostring(1 + select('#', ...)) .. ', expected at least 3)') + error( + 'wrong number of arguments (given ' + .. tostring(1 + select('#', ...)) + .. ', expected at least 3)' + ) end local ret = {} @@ -650,7 +654,8 @@ do -- Check user-provided validation function local valid, optional_message = types(val) if not valid then - local error_message = string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val)) + local error_message = + string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val)) if optional_message ~= nil then error_message = error_message .. string.format('. Info: %s', optional_message) end @@ -672,7 +677,13 @@ do end end if not success then - return false, string.format('%s: expected %s, got %s', param_name, table.concat(types, '|'), type(val)) + return false, + string.format( + '%s: expected %s, got %s', + param_name, + table.concat(types, '|'), + type(val) + ) end else return false, string.format('invalid type name: %s', tostring(types)) diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua index aa9db6b71b..3bd59ca282 100644 --- a/runtime/lua/vim/treesitter/health.lua +++ b/runtime/lua/vim/treesitter/health.lua @@ -26,7 +26,9 @@ function M.check() report_error(string.format('Impossible to load parser for %s: %s', parsername, ret)) elseif ret then local lang = ts.language.inspect_language(parsername) - report_ok(string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version)) + report_ok( + string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version) + ) else report_error(string.format('Unable to load parser for %s', parsername)) end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index d3da300e96..e27a5fa9c3 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -16,7 +16,7 @@ local _default_highlights = {} local _link_default_highlight_once = function(from, to) if not _default_highlights[from] then _default_highlights[from] = true - vim.cmd(string.format('highlight default link %s %s', from, to)) + a.nvim_set_hl(0, from, { link = to, default = true }) end return from @@ -280,7 +280,8 @@ local function on_line_impl(self, buf, line) end if state.iter == nil or state.next_row < line then - state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) + state.iter = + highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) end while line >= state.next_row do diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 767573e345..4d3b0631a2 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -32,10 +32,8 @@ function LanguageTree.new(source, lang, opts) _regions = {}, _trees = {}, _opts = opts, - _injection_query = injections[lang] and query.parse_query(lang, injections[lang]) or query.get_query( - lang, - 'injections' - ), + _injection_query = injections[lang] and query.parse_query(lang, injections[lang]) + or query.get_query(lang, 'injections'), _valid = false, _parser = vim._create_ts_parser(lang), _callbacks = { diff --git a/runtime/scripts.vim b/runtime/scripts.vim index a129c3467e..2d8bfdcb05 100644 --- a/runtime/scripts.vim +++ b/runtime/scripts.vim @@ -11,9 +11,13 @@ " 'ignorecase' option making a difference. Where case is to be ignored use " =~? instead. Do not use =~ anywhere. -" Only do the rest when not using Lua filetype detection -" and the FileType autocommand has not been triggered yet. -if exists("g:do_filetype_lua") && g:do_filetype_lua || did_filetype() +" Only run when using legacy filetype +if !exists('g:do_legacy_filetype') + finish +endif + +" Only do the rest when the FileType autocommand has not been triggered yet. +if did_filetype() finish endif diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 130ce65b86..06536e6e2b 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -5,6 +5,13 @@ /// /// Functions for Arabic language. /// +/// Author: Nadim Shaikli & Isam Bayazidi +/// Farsi support and restructuring to make adding new letters easier by Ali +/// Gholami Rudi. Further work by Ameretat Reith. + +/// Sorted list of unicode Arabic characters. Each entry holds the +/// presentation forms of a letter. +/// /// Arabic characters are categorized into following types: /// /// Isolated - iso-8859-6 form char denoted with a_* @@ -19,12 +26,7 @@ #include "nvim/ascii.h" #include "nvim/vim.h" -// Arabic ISO-10646-1 character set definition - -// Arabic ISO-8859-6 (subset of 10646; 0600 - 06FF) -#define a_COMMA 0x060C -#define a_SEMICOLON 0x061B -#define a_QUESTION 0x061F +// Unicode values for Arabic characters. #define a_HAMZA 0x0621 #define a_ALEF_MADDA 0x0622 #define a_ALEF_HAMZA_ABOVE 0x0623 @@ -62,7 +64,6 @@ #define a_WAW 0x0648 #define a_ALEF_MAKSURA 0x0649 #define a_YEH 0x064a - #define a_FATHATAN 0x064b #define a_DAMMATAN 0x064c #define a_KASRATAN 0x064d @@ -71,168 +72,17 @@ #define a_KASRA 0x0650 #define a_SHADDA 0x0651 #define a_SUKUN 0x0652 - #define a_MADDA_ABOVE 0x0653 #define a_HAMZA_ABOVE 0x0654 #define a_HAMZA_BELOW 0x0655 -#define a_ZERO 0x0660 -#define a_ONE 0x0661 -#define a_TWO 0x0662 -#define a_THREE 0x0663 -#define a_FOUR 0x0664 -#define a_FIVE 0x0665 -#define a_SIX 0x0666 -#define a_SEVEN 0x0667 -#define a_EIGHT 0x0668 -#define a_NINE 0x0669 -#define a_PERCENT 0x066a -#define a_DECIMAL 0x066b -#define a_THOUSANDS 0x066c -#define a_STAR 0x066d -#define a_MINI_ALEF 0x0670 -// Rest of 8859-6 does not relate to Arabic +#define a_PEH 0x067e +#define a_TCHEH 0x0686 +#define a_JEH 0x0698 +#define a_FKAF 0x06a9 +#define a_GAF 0x06af +#define a_FYEH 0x06cc -// Arabic Presentation Form-B (subset of 10646; FE70 - FEFF) -// -// s -> isolated -// i -> initial -// m -> medial -// f -> final -#define a_s_FATHATAN 0xfe70 -#define a_m_TATWEEL_FATHATAN 0xfe71 -#define a_s_DAMMATAN 0xfe72 - -#define a_s_KASRATAN 0xfe74 - -#define a_s_FATHA 0xfe76 -#define a_m_FATHA 0xfe77 -#define a_s_DAMMA 0xfe78 -#define a_m_DAMMA 0xfe79 -#define a_s_KASRA 0xfe7a -#define a_m_KASRA 0xfe7b -#define a_s_SHADDA 0xfe7c -#define a_m_SHADDA 0xfe7d -#define a_s_SUKUN 0xfe7e -#define a_m_SUKUN 0xfe7f - -#define a_s_HAMZA 0xfe80 -#define a_s_ALEF_MADDA 0xfe81 -#define a_f_ALEF_MADDA 0xfe82 -#define a_s_ALEF_HAMZA_ABOVE 0xfe83 -#define a_f_ALEF_HAMZA_ABOVE 0xfe84 -#define a_s_WAW_HAMZA 0xfe85 -#define a_f_WAW_HAMZA 0xfe86 -#define a_s_ALEF_HAMZA_BELOW 0xfe87 -#define a_f_ALEF_HAMZA_BELOW 0xfe88 -#define a_s_YEH_HAMZA 0xfe89 -#define a_f_YEH_HAMZA 0xfe8a -#define a_i_YEH_HAMZA 0xfe8b -#define a_m_YEH_HAMZA 0xfe8c -#define a_s_ALEF 0xfe8d -#define a_f_ALEF 0xfe8e -#define a_s_BEH 0xfe8f -#define a_f_BEH 0xfe90 -#define a_i_BEH 0xfe91 -#define a_m_BEH 0xfe92 -#define a_s_TEH_MARBUTA 0xfe93 -#define a_f_TEH_MARBUTA 0xfe94 -#define a_s_TEH 0xfe95 -#define a_f_TEH 0xfe96 -#define a_i_TEH 0xfe97 -#define a_m_TEH 0xfe98 -#define a_s_THEH 0xfe99 -#define a_f_THEH 0xfe9a -#define a_i_THEH 0xfe9b -#define a_m_THEH 0xfe9c -#define a_s_JEEM 0xfe9d -#define a_f_JEEM 0xfe9e -#define a_i_JEEM 0xfe9f -#define a_m_JEEM 0xfea0 -#define a_s_HAH 0xfea1 -#define a_f_HAH 0xfea2 -#define a_i_HAH 0xfea3 -#define a_m_HAH 0xfea4 -#define a_s_KHAH 0xfea5 -#define a_f_KHAH 0xfea6 -#define a_i_KHAH 0xfea7 -#define a_m_KHAH 0xfea8 -#define a_s_DAL 0xfea9 -#define a_f_DAL 0xfeaa -#define a_s_THAL 0xfeab -#define a_f_THAL 0xfeac -#define a_s_REH 0xfead -#define a_f_REH 0xfeae -#define a_s_ZAIN 0xfeaf -#define a_f_ZAIN 0xfeb0 -#define a_s_SEEN 0xfeb1 -#define a_f_SEEN 0xfeb2 -#define a_i_SEEN 0xfeb3 -#define a_m_SEEN 0xfeb4 -#define a_s_SHEEN 0xfeb5 -#define a_f_SHEEN 0xfeb6 -#define a_i_SHEEN 0xfeb7 -#define a_m_SHEEN 0xfeb8 -#define a_s_SAD 0xfeb9 -#define a_f_SAD 0xfeba -#define a_i_SAD 0xfebb -#define a_m_SAD 0xfebc -#define a_s_DAD 0xfebd -#define a_f_DAD 0xfebe -#define a_i_DAD 0xfebf -#define a_m_DAD 0xfec0 -#define a_s_TAH 0xfec1 -#define a_f_TAH 0xfec2 -#define a_i_TAH 0xfec3 -#define a_m_TAH 0xfec4 -#define a_s_ZAH 0xfec5 -#define a_f_ZAH 0xfec6 -#define a_i_ZAH 0xfec7 -#define a_m_ZAH 0xfec8 -#define a_s_AIN 0xfec9 -#define a_f_AIN 0xfeca -#define a_i_AIN 0xfecb -#define a_m_AIN 0xfecc -#define a_s_GHAIN 0xfecd -#define a_f_GHAIN 0xfece -#define a_i_GHAIN 0xfecf -#define a_m_GHAIN 0xfed0 -#define a_s_FEH 0xfed1 -#define a_f_FEH 0xfed2 -#define a_i_FEH 0xfed3 -#define a_m_FEH 0xfed4 -#define a_s_QAF 0xfed5 -#define a_f_QAF 0xfed6 -#define a_i_QAF 0xfed7 -#define a_m_QAF 0xfed8 -#define a_s_KAF 0xfed9 -#define a_f_KAF 0xfeda -#define a_i_KAF 0xfedb -#define a_m_KAF 0xfedc -#define a_s_LAM 0xfedd -#define a_f_LAM 0xfede -#define a_i_LAM 0xfedf -#define a_m_LAM 0xfee0 -#define a_s_MEEM 0xfee1 -#define a_f_MEEM 0xfee2 -#define a_i_MEEM 0xfee3 -#define a_m_MEEM 0xfee4 -#define a_s_NOON 0xfee5 -#define a_f_NOON 0xfee6 -#define a_i_NOON 0xfee7 -#define a_m_NOON 0xfee8 -#define a_s_HEH 0xfee9 -#define a_f_HEH 0xfeea -#define a_i_HEH 0xfeeb -#define a_m_HEH 0xfeec -#define a_s_WAW 0xfeed -#define a_f_WAW 0xfeee -#define a_s_ALEF_MAKSURA 0xfeef -#define a_f_ALEF_MAKSURA 0xfef0 -#define a_s_YEH 0xfef1 -#define a_f_YEH 0xfef2 -#define a_i_YEH 0xfef3 -#define a_m_YEH 0xfef4 #define a_s_LAM_ALEF_MADDA_ABOVE 0xfef5 #define a_f_LAM_ALEF_MADDA_ABOVE 0xfef6 #define a_s_LAM_ALEF_HAMZA_ABOVE 0xfef7 @@ -242,664 +92,201 @@ #define a_s_LAM_ALEF 0xfefb #define a_f_LAM_ALEF 0xfefc +static struct achar { + unsigned c; + unsigned isolated; + unsigned initial; + unsigned medial; + unsigned final; +} achars[] = { + { a_HAMZA, 0xfe80, 0, 0, 0 }, + { a_ALEF_MADDA, 0xfe81, 0, 0, 0xfe82 }, + { a_ALEF_HAMZA_ABOVE, 0xfe83, 0, 0, 0xfe84 }, + { a_WAW_HAMZA, 0xfe85, 0, 0, 0xfe86 }, + { a_ALEF_HAMZA_BELOW, 0xfe87, 0, 0, 0xfe88 }, + { a_YEH_HAMZA, 0xfe89, 0xfe8b, 0xfe8c, 0xfe8a }, + { a_ALEF, 0xfe8d, 0, 0, 0xfe8e }, + { a_BEH, 0xfe8f, 0xfe91, 0xfe92, 0xfe90 }, + { a_TEH_MARBUTA, 0xfe93, 0, 0, 0xfe94 }, + { a_TEH, 0xfe95, 0xfe97, 0xfe98, 0xfe96 }, + { a_THEH, 0xfe99, 0xfe9b, 0xfe9c, 0xfe9a }, + { a_JEEM, 0xfe9d, 0xfe9f, 0xfea0, 0xfe9e }, + { a_HAH, 0xfea1, 0xfea3, 0xfea4, 0xfea2 }, + { a_KHAH, 0xfea5, 0xfea7, 0xfea8, 0xfea6 }, + { a_DAL, 0xfea9, 0, 0, 0xfeaa }, + { a_THAL, 0xfeab, 0, 0, 0xfeac }, + { a_REH, 0xfead, 0, 0, 0xfeae }, + { a_ZAIN, 0xfeaf, 0, 0, 0xfeb0 }, + { a_SEEN, 0xfeb1, 0xfeb3, 0xfeb4, 0xfeb2 }, + { a_SHEEN, 0xfeb5, 0xfeb7, 0xfeb8, 0xfeb6 }, + { a_SAD, 0xfeb9, 0xfebb, 0xfebc, 0xfeba }, + { a_DAD, 0xfebd, 0xfebf, 0xfec0, 0xfebe }, + { a_TAH, 0xfec1, 0xfec3, 0xfec4, 0xfec2 }, + { a_ZAH, 0xfec5, 0xfec7, 0xfec8, 0xfec6 }, + { a_AIN, 0xfec9, 0xfecb, 0xfecc, 0xfeca }, + { a_GHAIN, 0xfecd, 0xfecf, 0xfed0, 0xfece }, + { a_TATWEEL, 0, 0x0640, 0x0640, 0x0640 }, + { a_FEH, 0xfed1, 0xfed3, 0xfed4, 0xfed2 }, + { a_QAF, 0xfed5, 0xfed7, 0xfed8, 0xfed6 }, + { a_KAF, 0xfed9, 0xfedb, 0xfedc, 0xfeda }, + { a_LAM, 0xfedd, 0xfedf, 0xfee0, 0xfede }, + { a_MEEM, 0xfee1, 0xfee3, 0xfee4, 0xfee2 }, + { a_NOON, 0xfee5, 0xfee7, 0xfee8, 0xfee6 }, + { a_HEH, 0xfee9, 0xfeeb, 0xfeec, 0xfeea }, + { a_WAW, 0xfeed, 0, 0, 0xfeee }, + { a_ALEF_MAKSURA, 0xfeef, 0, 0, 0xfef0 }, + { a_YEH, 0xfef1, 0xfef3, 0xfef4, 0xfef2 }, + { a_FATHATAN, 0xfe70, 0, 0, 0 }, + { a_DAMMATAN, 0xfe72, 0, 0, 0 }, + { a_KASRATAN, 0xfe74, 0, 0, 0 }, + { a_FATHA, 0xfe76, 0, 0xfe77, 0 }, + { a_DAMMA, 0xfe78, 0, 0xfe79, 0 }, + { a_KASRA, 0xfe7a, 0, 0xfe7b, 0 }, + { a_SHADDA, 0xfe7c, 0, 0xfe7c, 0 }, + { a_SUKUN, 0xfe7e, 0, 0xfe7f, 0 }, + { a_MADDA_ABOVE, 0, 0, 0, 0 }, + { a_HAMZA_ABOVE, 0, 0, 0, 0 }, + { a_HAMZA_BELOW, 0, 0, 0, 0 }, + { a_PEH, 0xfb56, 0xfb58, 0xfb59, 0xfb57 }, + { a_TCHEH, 0xfb7a, 0xfb7c, 0xfb7d, 0xfb7b }, + { a_JEH, 0xfb8a, 0, 0, 0xfb8b }, + { a_FKAF, 0xfb8e, 0xfb90, 0xfb91, 0xfb8f }, + { a_GAF, 0xfb92, 0xfb94, 0xfb95, 0xfb93 }, + { a_FYEH, 0xfbfc, 0xfbfe, 0xfbff, 0xfbfd }, +}; + #define a_BYTE_ORDER_MARK 0xfeff #ifdef INCLUDE_GENERATED_DECLARATIONS # include "arabic.c.generated.h" #endif -// Returns true if c is an ISO-8859-6 shaped ARABIC letter (user entered). -static bool A_is_a(int cur_c) -{ - switch (cur_c) { - case a_HAMZA: - case a_ALEF_MADDA: - case a_ALEF_HAMZA_ABOVE: - case a_WAW_HAMZA: - case a_ALEF_HAMZA_BELOW: - case a_YEH_HAMZA: - case a_ALEF: - case a_BEH: - case a_TEH_MARBUTA: - case a_TEH: - case a_THEH: - case a_JEEM: - case a_HAH: - case a_KHAH: - case a_DAL: - case a_THAL: - case a_REH: - case a_ZAIN: - case a_SEEN: - case a_SHEEN: - case a_SAD: - case a_DAD: - case a_TAH: - case a_ZAH: - case a_AIN: - case a_GHAIN: - case a_TATWEEL: - case a_FEH: - case a_QAF: - case a_KAF: - case a_LAM: - case a_MEEM: - case a_NOON: - case a_HEH: - case a_WAW: - case a_ALEF_MAKSURA: - case a_YEH: - return true; - } - - return false; -} - -// Returns true if c is an Isolated Form-B ARABIC letter -static bool A_is_s(int cur_c) +/// Find the struct achar pointer to the given Arabic char. +/// Returns NULL if not found. +static struct achar *find_achar(int c) { - switch (cur_c) { - case a_s_HAMZA: - case a_s_ALEF_MADDA: - case a_s_ALEF_HAMZA_ABOVE: - case a_s_WAW_HAMZA: - case a_s_ALEF_HAMZA_BELOW: - case a_s_YEH_HAMZA: - case a_s_ALEF: - case a_s_BEH: - case a_s_TEH_MARBUTA: - case a_s_TEH: - case a_s_THEH: - case a_s_JEEM: - case a_s_HAH: - case a_s_KHAH: - case a_s_DAL: - case a_s_THAL: - case a_s_REH: - case a_s_ZAIN: - case a_s_SEEN: - case a_s_SHEEN: - case a_s_SAD: - case a_s_DAD: - case a_s_TAH: - case a_s_ZAH: - case a_s_AIN: - case a_s_GHAIN: - case a_s_FEH: - case a_s_QAF: - case a_s_KAF: - case a_s_LAM: - case a_s_MEEM: - case a_s_NOON: - case a_s_HEH: - case a_s_WAW: - case a_s_ALEF_MAKSURA: - case a_s_YEH: - return true; + // using binary search to find c + int h = ARRAY_SIZE(achars); + int l = 0; + while (l < h) { + int m = (h + l) / 2; + if (achars[m].c == (unsigned)c) { + return &achars[m]; + } + if ((unsigned)c < achars[m].c) { + h = m; + } else { + l = m + 1; + } } - - return false; + return NULL; } -// Returns true if c is a Final shape of an ARABIC letter -static bool A_is_f(int cur_c) +/// Change shape - from Combination (2 char) to an Isolated +static int chg_c_laa2i(int hid_c) { - switch (cur_c) { - case a_f_ALEF_MADDA: - case a_f_ALEF_HAMZA_ABOVE: - case a_f_WAW_HAMZA: - case a_f_ALEF_HAMZA_BELOW: - case a_f_YEH_HAMZA: - case a_f_ALEF: - case a_f_BEH: - case a_f_TEH_MARBUTA: - case a_f_TEH: - case a_f_THEH: - case a_f_JEEM: - case a_f_HAH: - case a_f_KHAH: - case a_f_DAL: - case a_f_THAL: - case a_f_REH: - case a_f_ZAIN: - case a_f_SEEN: - case a_f_SHEEN: - case a_f_SAD: - case a_f_DAD: - case a_f_TAH: - case a_f_ZAH: - case a_f_AIN: - case a_f_GHAIN: - case a_f_FEH: - case a_f_QAF: - case a_f_KAF: - case a_f_LAM: - case a_f_MEEM: - case a_f_NOON: - case a_f_HEH: - case a_f_WAW: - case a_f_ALEF_MAKSURA: - case a_f_YEH: - case a_f_LAM_ALEF_MADDA_ABOVE: - case a_f_LAM_ALEF_HAMZA_ABOVE: - case a_f_LAM_ALEF_HAMZA_BELOW: - case a_f_LAM_ALEF: - return true; - } - return false; -} + int tempc; -// Change shape - from ISO-8859-6/Isolated to Form-B Isolated -static int chg_c_a2s(int cur_c) -{ - switch (cur_c) { - case a_HAMZA: - return a_s_HAMZA; + switch (hid_c) { case a_ALEF_MADDA: - return a_s_ALEF_MADDA; + tempc = a_s_LAM_ALEF_MADDA_ABOVE; + break; case a_ALEF_HAMZA_ABOVE: - return a_s_ALEF_HAMZA_ABOVE; - case a_WAW_HAMZA: - return a_s_WAW_HAMZA; + tempc = a_s_LAM_ALEF_HAMZA_ABOVE; + break; case a_ALEF_HAMZA_BELOW: - return a_s_ALEF_HAMZA_BELOW; - case a_YEH_HAMZA: - return a_s_YEH_HAMZA; + tempc = a_s_LAM_ALEF_HAMZA_BELOW; + break; case a_ALEF: - return a_s_ALEF; - case a_TEH_MARBUTA: - return a_s_TEH_MARBUTA; - case a_DAL: - return a_s_DAL; - case a_THAL: - return a_s_THAL; - case a_REH: - return a_s_REH; - case a_ZAIN: - return a_s_ZAIN; - case a_TATWEEL: - return cur_c; // exceptions - case a_WAW: - return a_s_WAW; - case a_ALEF_MAKSURA: - return a_s_ALEF_MAKSURA; - case a_BEH: - return a_s_BEH; - case a_TEH: - return a_s_TEH; - case a_THEH: - return a_s_THEH; - case a_JEEM: - return a_s_JEEM; - case a_HAH: - return a_s_HAH; - case a_KHAH: - return a_s_KHAH; - case a_SEEN: - return a_s_SEEN; - case a_SHEEN: - return a_s_SHEEN; - case a_SAD: - return a_s_SAD; - case a_DAD: - return a_s_DAD; - case a_TAH: - return a_s_TAH; - case a_ZAH: - return a_s_ZAH; - case a_AIN: - return a_s_AIN; - case a_GHAIN: - return a_s_GHAIN; - case a_FEH: - return a_s_FEH; - case a_QAF: - return a_s_QAF; - case a_KAF: - return a_s_KAF; - case a_LAM: - return a_s_LAM; - case a_MEEM: - return a_s_MEEM; - case a_NOON: - return a_s_NOON; - case a_HEH: - return a_s_HEH; - case a_YEH: - return a_s_YEH; + tempc = a_s_LAM_ALEF; + break; + default: + tempc = 0; } - return 0; -} -// Change shape - from ISO-8859-6/Isolated to Initial -static int chg_c_a2i(int cur_c) -{ - switch (cur_c) { - case a_YEH_HAMZA: - return a_i_YEH_HAMZA; - case a_HAMZA: - return a_s_HAMZA; // exceptions - case a_ALEF_MADDA: - return a_s_ALEF_MADDA; // exceptions - case a_ALEF_HAMZA_ABOVE: - return a_s_ALEF_HAMZA_ABOVE; // exceptions - case a_WAW_HAMZA: - return a_s_WAW_HAMZA; // exceptions - case a_ALEF_HAMZA_BELOW: - return a_s_ALEF_HAMZA_BELOW; // exceptions - case a_ALEF: - return a_s_ALEF; // exceptions - case a_TEH_MARBUTA: - return a_s_TEH_MARBUTA; // exceptions - case a_DAL: - return a_s_DAL; // exceptions - case a_THAL: - return a_s_THAL; // exceptions - case a_REH: - return a_s_REH; // exceptions - case a_ZAIN: - return a_s_ZAIN; // exceptions - case a_TATWEEL: - return cur_c; // exceptions - case a_WAW: - return a_s_WAW; // exceptions - case a_ALEF_MAKSURA: - return a_s_ALEF_MAKSURA; // exceptions - case a_BEH: - return a_i_BEH; - case a_TEH: - return a_i_TEH; - case a_THEH: - return a_i_THEH; - case a_JEEM: - return a_i_JEEM; - case a_HAH: - return a_i_HAH; - case a_KHAH: - return a_i_KHAH; - case a_SEEN: - return a_i_SEEN; - case a_SHEEN: - return a_i_SHEEN; - case a_SAD: - return a_i_SAD; - case a_DAD: - return a_i_DAD; - case a_TAH: - return a_i_TAH; - case a_ZAH: - return a_i_ZAH; - case a_AIN: - return a_i_AIN; - case a_GHAIN: - return a_i_GHAIN; - case a_FEH: - return a_i_FEH; - case a_QAF: - return a_i_QAF; - case a_KAF: - return a_i_KAF; - case a_LAM: - return a_i_LAM; - case a_MEEM: - return a_i_MEEM; - case a_NOON: - return a_i_NOON; - case a_HEH: - return a_i_HEH; - case a_YEH: - return a_i_YEH; - } - return 0; + return tempc; } -// Change shape - from ISO-8859-6/Isolated to Medial -static int chg_c_a2m(int cur_c) +/// Change shape - from Combination-Isolated to Final +static int chg_c_laa2f(int hid_c) { - switch (cur_c) { - case a_HAMZA: - return a_s_HAMZA; // exception + int tempc; + + switch (hid_c) { case a_ALEF_MADDA: - return a_f_ALEF_MADDA; // exception + tempc = a_f_LAM_ALEF_MADDA_ABOVE; + break; case a_ALEF_HAMZA_ABOVE: - return a_f_ALEF_HAMZA_ABOVE; // exception - case a_WAW_HAMZA: - return a_f_WAW_HAMZA; // exception + tempc = a_f_LAM_ALEF_HAMZA_ABOVE; + break; case a_ALEF_HAMZA_BELOW: - return a_f_ALEF_HAMZA_BELOW; // exception - case a_YEH_HAMZA: - return a_m_YEH_HAMZA; + tempc = a_f_LAM_ALEF_HAMZA_BELOW; + break; case a_ALEF: - return a_f_ALEF; // exception - case a_BEH: - return a_m_BEH; - case a_TEH_MARBUTA: - return a_f_TEH_MARBUTA; // exception - case a_TEH: - return a_m_TEH; - case a_THEH: - return a_m_THEH; - case a_JEEM: - return a_m_JEEM; - case a_HAH: - return a_m_HAH; - case a_KHAH: - return a_m_KHAH; - case a_DAL: - return a_f_DAL; // exception - case a_THAL: - return a_f_THAL; // exception - case a_REH: - return a_f_REH; // exception - case a_ZAIN: - return a_f_ZAIN; // exception - case a_SEEN: - return a_m_SEEN; - case a_SHEEN: - return a_m_SHEEN; - case a_SAD: - return a_m_SAD; - case a_DAD: - return a_m_DAD; - case a_TAH: - return a_m_TAH; - case a_ZAH: - return a_m_ZAH; - case a_AIN: - return a_m_AIN; - case a_GHAIN: - return a_m_GHAIN; - case a_TATWEEL: - return cur_c; // exception - case a_FEH: - return a_m_FEH; - case a_QAF: - return a_m_QAF; - case a_KAF: - return a_m_KAF; - case a_LAM: - return a_m_LAM; - case a_MEEM: - return a_m_MEEM; - case a_NOON: - return a_m_NOON; - case a_HEH: - return a_m_HEH; - case a_WAW: - return a_f_WAW; // exception - case a_ALEF_MAKSURA: - return a_f_ALEF_MAKSURA; // exception - case a_YEH: - return a_m_YEH; + tempc = a_f_LAM_ALEF; + break; + default: + tempc = 0; } - return 0; + + return tempc; } -// Change shape - from ISO-8859-6/Isolated to final -static int chg_c_a2f(int cur_c) +/// Returns whether it is possible to join the given letters +static int can_join(int c1, int c2) { - // NOTE: these encodings need to be accounted for - // - // a_f_ALEF_MADDA; - // a_f_ALEF_HAMZA_ABOVE; - // a_f_ALEF_HAMZA_BELOW; - // a_f_LAM_ALEF_MADDA_ABOVE; - // a_f_LAM_ALEF_HAMZA_ABOVE; - // a_f_LAM_ALEF_HAMZA_BELOW; + struct achar *a1 = find_achar(c1); + struct achar *a2 = find_achar(c2); - switch (cur_c) { - case a_HAMZA: - return a_s_HAMZA; // exception - case a_ALEF_MADDA: - return a_f_ALEF_MADDA; - case a_ALEF_HAMZA_ABOVE: - return a_f_ALEF_HAMZA_ABOVE; - case a_WAW_HAMZA: - return a_f_WAW_HAMZA; - case a_ALEF_HAMZA_BELOW: - return a_f_ALEF_HAMZA_BELOW; - case a_YEH_HAMZA: - return a_f_YEH_HAMZA; - case a_ALEF: - return a_f_ALEF; - case a_BEH: - return a_f_BEH; - case a_TEH_MARBUTA: - return a_f_TEH_MARBUTA; - case a_TEH: - return a_f_TEH; - case a_THEH: - return a_f_THEH; - case a_JEEM: - return a_f_JEEM; - case a_HAH: - return a_f_HAH; - case a_KHAH: - return a_f_KHAH; - case a_DAL: - return a_f_DAL; - case a_THAL: - return a_f_THAL; - case a_REH: - return a_f_REH; - case a_ZAIN: - return a_f_ZAIN; - case a_SEEN: - return a_f_SEEN; - case a_SHEEN: - return a_f_SHEEN; - case a_SAD: - return a_f_SAD; - case a_DAD: - return a_f_DAD; - case a_TAH: - return a_f_TAH; - case a_ZAH: - return a_f_ZAH; - case a_AIN: - return a_f_AIN; - case a_GHAIN: - return a_f_GHAIN; - case a_TATWEEL: - return cur_c; // exception - case a_FEH: - return a_f_FEH; - case a_QAF: - return a_f_QAF; - case a_KAF: - return a_f_KAF; - case a_LAM: - return a_f_LAM; - case a_MEEM: - return a_f_MEEM; - case a_NOON: - return a_f_NOON; - case a_HEH: - return a_f_HEH; - case a_WAW: - return a_f_WAW; - case a_ALEF_MAKSURA: - return a_f_ALEF_MAKSURA; - case a_YEH: - return a_f_YEH; - } - return 0; + return a1 && a2 && (a1->initial || a1->medial) && (a2->final || a2->medial); } -// Change shape - from Initial to Medial -// This code is unreachable, because for the relevant characters ARABIC_CHAR() -// is FALSE; -#if 0 -static int chg_c_i2m(int cur_c) +/// Check whether we are dealing with a character that could be regarded as an +/// Arabic combining character, need to check the character before this. +bool arabic_maycombine(int two) + FUNC_ATTR_PURE { - switch (cur_c) { - case a_i_YEH_HAMZA: - return a_m_YEH_HAMZA; - case a_i_BEH: - return a_m_BEH; - case a_i_TEH: - return a_m_TEH; - case a_i_THEH: - return a_m_THEH; - case a_i_JEEM: - return a_m_JEEM; - case a_i_HAH: - return a_m_HAH; - case a_i_KHAH: - return a_m_KHAH; - case a_i_SEEN: - return a_m_SEEN; - case a_i_SHEEN: - return a_m_SHEEN; - case a_i_SAD: - return a_m_SAD; - case a_i_DAD: - return a_m_DAD; - case a_i_TAH: - return a_m_TAH; - case a_i_ZAH: - return a_m_ZAH; - case a_i_AIN: - return a_m_AIN; - case a_i_GHAIN: - return a_m_GHAIN; - case a_i_FEH: - return a_m_FEH; - case a_i_QAF: - return a_m_QAF; - case a_i_KAF: - return a_m_KAF; - case a_i_LAM: - return a_m_LAM; - case a_i_MEEM: - return a_m_MEEM; - case a_i_NOON: - return a_m_NOON; - case a_i_HEH: - return a_m_HEH; - case a_i_YEH: - return a_m_YEH; + if (p_arshape && !p_tbidi) { + return two == a_ALEF_MADDA + || two == a_ALEF_HAMZA_ABOVE + || two == a_ALEF_HAMZA_BELOW + || two == a_ALEF; } - return 0; + return false; } -#endif -// Change shape - from Final to Medial -static int chg_c_f2m(int cur_c) +/// Check whether we are dealing with Arabic combining characters. +/// Note: these are NOT really composing characters! +/// +/// @param one First character. +/// @param two Character just after "one". +bool arabic_combine(int one, int two) + FUNC_ATTR_PURE { - switch (cur_c) { - // NOTE: these encodings are multi-positional, no ? - // case a_f_ALEF_MADDA: - // case a_f_ALEF_HAMZA_ABOVE: - // case a_f_ALEF_HAMZA_BELOW: - case a_f_YEH_HAMZA: - return a_m_YEH_HAMZA; - case a_f_WAW_HAMZA: // exceptions - case a_f_ALEF: - case a_f_TEH_MARBUTA: - case a_f_DAL: - case a_f_THAL: - case a_f_REH: - case a_f_ZAIN: - case a_f_WAW: - case a_f_ALEF_MAKSURA: - return cur_c; - case a_f_BEH: - return a_m_BEH; - case a_f_TEH: - return a_m_TEH; - case a_f_THEH: - return a_m_THEH; - case a_f_JEEM: - return a_m_JEEM; - case a_f_HAH: - return a_m_HAH; - case a_f_KHAH: - return a_m_KHAH; - case a_f_SEEN: - return a_m_SEEN; - case a_f_SHEEN: - return a_m_SHEEN; - case a_f_SAD: - return a_m_SAD; - case a_f_DAD: - return a_m_DAD; - case a_f_TAH: - return a_m_TAH; - case a_f_ZAH: - return a_m_ZAH; - case a_f_AIN: - return a_m_AIN; - case a_f_GHAIN: - return a_m_GHAIN; - case a_f_FEH: - return a_m_FEH; - case a_f_QAF: - return a_m_QAF; - case a_f_KAF: - return a_m_KAF; - case a_f_LAM: - return a_m_LAM; - case a_f_MEEM: - return a_m_MEEM; - case a_f_NOON: - return a_m_NOON; - case a_f_HEH: - return a_m_HEH; - case a_f_YEH: - return a_m_YEH; - // NOTE: these encodings are multi-positional, no ? - // case a_f_LAM_ALEF_MADDA_ABOVE: - // case a_f_LAM_ALEF_HAMZA_ABOVE: - // case a_f_LAM_ALEF_HAMZA_BELOW: - // case a_f_LAM_ALEF: + if (one == a_LAM) { + return arabic_maycombine(two); } - return 0; + return false; } -// Change shape - from Combination (2 char) to an Isolated. -static int chg_c_laa2i(int hid_c) +/// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character +/// (alphabet/number/punctuation) +static int A_is_iso(int c) { - switch (hid_c) { - case a_ALEF_MADDA: - return a_s_LAM_ALEF_MADDA_ABOVE; - case a_ALEF_HAMZA_ABOVE: - return a_s_LAM_ALEF_HAMZA_ABOVE; - case a_ALEF_HAMZA_BELOW: - return a_s_LAM_ALEF_HAMZA_BELOW; - case a_ALEF: - return a_s_LAM_ALEF; - } - return 0; + return find_achar(c) != NULL; } -// Change shape - from Combination-Isolated to Final. -static int chg_c_laa2f(int hid_c) +/// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B) +static int A_is_ok(int c) { - switch (hid_c) { - case a_ALEF_MADDA: - return a_f_LAM_ALEF_MADDA_ABOVE; - case a_ALEF_HAMZA_ABOVE: - return a_f_LAM_ALEF_HAMZA_ABOVE; - case a_ALEF_HAMZA_BELOW: - return a_f_LAM_ALEF_HAMZA_BELOW; - case a_ALEF: - return a_f_LAM_ALEF; - } - return 0; + return (A_is_iso(c) || c == a_BYTE_ORDER_MARK); } -// Do "half-shaping" on character "c". Return zero if no shaping. -static int half_shape(int c) +/// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B) +/// with some exceptions/exclusions +static int A_is_valid(int c) { - if (A_is_a(c)) { - return chg_c_a2i(c); - } - - if (A_is_valid(c) && A_is_f(c)) { - return chg_c_f2m(c); - } - return 0; + return (A_is_ok(c) && c != a_HAMZA); } // Do Arabic shaping on character "c". Returns the shaped character. @@ -916,37 +303,35 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c) return c; } - // half-shape current and previous character - int shape_c = half_shape(prev_c); - int curr_c; - int curr_laa = A_firstc_laa(c, *c1p); - int prev_laa = A_firstc_laa(prev_c, prev_c1); + int curr_laa = arabic_combine(c, *c1p); + int prev_laa = arabic_combine(prev_c, prev_c1); if (curr_laa) { - if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) - && !prev_laa) { - curr_c = chg_c_laa2f(curr_laa); + if (A_is_valid(prev_c) && can_join(prev_c, a_LAM) && !prev_laa) { + curr_c = chg_c_laa2f(*c1p); } else { - curr_c = chg_c_laa2i(curr_laa); + curr_c = chg_c_laa2i(*c1p); } - // Remove the composing character *c1p = 0; - } else if (!A_is_valid(prev_c) && A_is_valid(next_c)) { - curr_c = chg_c_a2i(c); - } else if (!shape_c || A_is_f(shape_c) || A_is_s(shape_c) || prev_laa) { - curr_c = A_is_valid(next_c) ? chg_c_a2i(c) : chg_c_a2s(c); - } else if (A_is_valid(next_c)) { -#if 0 - curr_c = A_is_iso(c) ? chg_c_a2m(c) : chg_c_i2m(c); -#else - curr_c = A_is_iso(c) ? chg_c_a2m(c) : 0; -#endif - } else if (A_is_valid(prev_c)) { - curr_c = chg_c_a2f(c); } else { - curr_c = chg_c_a2s(c); + struct achar *curr_a = find_achar(c); + int backward_combine = !prev_laa && can_join(prev_c, c); + int forward_combine = can_join(c, next_c); + + if (backward_combine && forward_combine) { + curr_c = (int)curr_a->medial; + } + if (backward_combine && !forward_combine) { + curr_c = (int)curr_a->final; + } + if (!backward_combine && forward_combine) { + curr_c = (int)curr_a->initial; + } + if (!backward_combine && !forward_combine) { + curr_c = (int)curr_a->isolated; + } } // Sanity check -- curr_c should, in the future, never be 0. @@ -966,88 +351,3 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c) // Return the shaped character return curr_c; } - -/// Check whether we are dealing with Arabic combining characters. -/// Note: these are NOT really composing characters! -/// -/// @param one First character. -/// @param two Character just after "one". -bool arabic_combine(int one, int two) - FUNC_ATTR_PURE -{ - if (one == a_LAM) { - return arabic_maycombine(two); - } - return false; -} - -/// Check whether we are dealing with a character that could be regarded as an -/// Arabic combining character, need to check the character before this. -bool arabic_maycombine(int two) - FUNC_ATTR_PURE -{ - if (p_arshape && !p_tbidi) { - return two == a_ALEF_MADDA - || two == a_ALEF_HAMZA_ABOVE - || two == a_ALEF_HAMZA_BELOW - || two == a_ALEF; - } - return false; -} - -// A_firstc_laa returns first character of LAA combination if it exists -// in: "c" base character -// in: "c1" first composing character -static int A_firstc_laa(int c, int c1) -{ - if ((c1 != NUL) && (c == a_LAM) && !A_is_harakat(c1)) { - return c1; - } - return 0; -} - -// A_is_harakat returns true if 'c' is an Arabic Harakat character. -// (harakat/tanween) -static bool A_is_harakat(int c) -{ - return c >= a_FATHATAN && c <= a_SUKUN; -} - -// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character. -// (alphabet/number/punctuation) -static bool A_is_iso(int c) -{ - return ((c >= a_HAMZA && c <= a_GHAIN) - || (c >= a_TATWEEL && c <= a_HAMZA_BELOW) - || c == a_MINI_ALEF); -} - -// A_is_formb returns true if 'c' is an Arabic 10646-1 FormB character. -// (alphabet/number/punctuation) -static bool A_is_formb(int c) -{ - return ((c >= a_s_FATHATAN && c <= a_s_DAMMATAN) - || c == a_s_KASRATAN - || (c >= a_s_FATHA && c <= a_f_LAM_ALEF) - || c == a_BYTE_ORDER_MARK); -} - -// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B). -static bool A_is_ok(int c) -{ - return A_is_iso(c) || A_is_formb(c); -} - -// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B), -// with some exceptions/exclusions. -static bool A_is_valid(int c) -{ - return A_is_ok(c) && !A_is_special(c); -} - -// A_is_special returns true if 'c' is not a special Arabic character. -// Specials don't adhere to most of the rules. -static bool A_is_special(int c) -{ - return c == a_HAMZA || c == a_s_HAMZA; -} diff --git a/src/nvim/arabic.h b/src/nvim/arabic.h index eaab463777..3c34de1449 100644 --- a/src/nvim/arabic.h +++ b/src/nvim/arabic.h @@ -3,12 +3,7 @@ #include <stdbool.h> -/// Whether c belongs to the range of Arabic characters that might be shaped. -static inline bool arabic_char(int c) -{ - // return c >= a_HAMZA && c <= a_MINI_ALEF; - return c >= 0x0621 && c <= 0x0670; -} +#define ARABIC_CHAR(ch) (((ch) & 0xFF00) == 0x0600) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "arabic.h.generated.h" diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index defe22ea9a..897928abec 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2156,31 +2156,23 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq, .sc_lnum = 0, .sc_sid = 0 }; - FileID file_id; scriptitem_T *si = NULL; - bool file_id_ok = os_fileid((char *)fname, &file_id); assert(script_items.ga_len >= 0); - for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; - script_sctx.sc_sid--) { + for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) { + // We used to check inode here, but that doesn't work: + // - If a script is edited and written, it may get a different + // inode number, even though to the user it is the same script. + // - If a script is deleted and another script is written, with a + // different name, the inode may be re-used. si = &SCRIPT_ITEM(script_sctx.sc_sid); - // Compare dev/ino when possible, it catches symbolic links. - // Also compare file names, the inode may change when the file was edited. - bool file_id_equal = file_id_ok && si->file_id_valid - && os_fileid_equal(&(si->file_id), &file_id); - if (si->sn_name != NULL - && (file_id_equal || FNAMECMP(si->sn_name, fname) == 0)) { + if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) { + // Found it! break; } } if (script_sctx.sc_sid == 0) { si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid); - if (file_id_ok) { - si->file_id_valid = true; - si->file_id = file_id; - } else { - si->file_id_valid = false; - } } if (ret_sctx != NULL) { *ret_sctx = script_sctx; diff --git a/src/nvim/ex_cmds2.h b/src/nvim/ex_cmds2.h index 74e52dfb4b..c463bfa5ab 100644 --- a/src/nvim/ex_cmds2.h +++ b/src/nvim/ex_cmds2.h @@ -15,12 +15,8 @@ #define CCGD_ALLBUF 8 // may write all buffers #define CCGD_EXCMD 16 // may suggest using ! -/// Also store the dev/ino, so that we don't have to stat() each -/// script when going through the list. typedef struct scriptitem_S { char_u *sn_name; - bool file_id_valid; - FileID file_id; bool sn_prof_on; ///< true when script is/was profiled bool sn_pr_force; ///< forceit: profile functions in this script proftime_T sn_pr_child; ///< time set when going into first child diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d4fe55392e..2899e17039 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7899,9 +7899,11 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) need_wait_return = false; msg_scroll = 0; redraw_all_later(NOT_VALID); + pending_exmode_active = true; normal_enter(false, true); + pending_exmode_active = false; RedrawingDisabled = rd; no_wait_return = nwr; msg_scroll = ms; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 7e22ed55cb..32977569c3 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3224,7 +3224,7 @@ static void draw_cmdline(int start, int len) int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); - if (arabic_char(u8c)) { + if (ARABIC_CHAR(u8c)) { do_arabicshape = true; break; } @@ -3260,7 +3260,7 @@ static void draw_cmdline(int start, int len) int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); - if (arabic_char(u8c)) { + if (ARABIC_CHAR(u8c)) { int pc; int pc1 = 0; int nc = 0; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 0f6eddef9d..00372d4f3d 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2475,6 +2475,11 @@ static int vgetorpeek(bool advance) } tc = c; + // return 0 in normal_check() + if (pending_exmode_active) { + exmode_active = true; + } + // no chars to block abbreviations for typebuf.tb_no_abbr_cnt = 0; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 0281eebcee..01d4ab086e 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -636,6 +636,10 @@ EXTERN int motion_force INIT(=0); // motion force for pending operator // Ex Mode (Q) state EXTERN bool exmode_active INIT(= false); // true if Ex mode is active + +/// Flag set when normal_check() should return 0 when entering Ex mode. +EXTERN bool pending_exmode_active INIT(= false); + EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p. // 'inccommand' command preview state diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 7d407bd3d1..1268f987e1 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -241,7 +241,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col u8c = utfc_ptr2char(ptr, u8cc); } mbyte_cells = utf_char2cells(u8c); - if (p_arshape && !p_tbidi && arabic_char(u8c)) { + if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) { // Past end of string to be displayed. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 32e2d515e1..03d7cb1783 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1869,7 +1869,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b schar_from_ascii(dest[0], *p); s->prev_c = u8c; } else { - if (p_arshape && !p_tbidi && arabic_char(u8c)) { + if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. int pc, pc1, nc; int pcc[MAX_MCO]; @@ -3157,7 +3157,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } else if (mb_l == 0) { // at the NUL at end-of-line mb_l = 1; - } else if (p_arshape && !p_tbidi && arabic_char(mb_c)) { + } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) { // Do Arabic shaping. int pc, pc1, nc; int pcc[MAX_MCO]; diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index b0d872e392..d0a666a049 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -191,6 +191,9 @@ func RunTheTest(test) endtry endif + " Align Nvim defaults to Vim. + source setup.vim + " Clear any autocommands and put back the catch-all for SwapExists. au! au SwapExists * call HandleSwapExists() diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index dfcde37f62..f8db2ea120 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -1,39 +1,46 @@ -" Common preparations for running tests. - -" Only load this once. -if exists('s:did_load') - finish -endif -let s:did_load = 1 - " Align Nvim defaults to Vim. set backspace= +set complete=.,w,b,u,t,i +set directory& set directory^=. set fillchars=vert:\|,fold:- +set formatoptions=tcq set fsync set laststatus=1 set listchars=eol:$ set joinspaces -set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd -set nrformats+=octal -set shortmess-=F +set nohidden nosmarttab noautoindent noautoread noruler noshowcmd +set nohlsearch noincsearch +set nrformats=bin,octal,hex +set shortmess=filnxtToOS set sidescroll=0 set tags=./tags,tags +set undodir& set undodir^=. set wildoptions= set startofline +set sessionoptions& set sessionoptions+=options +set viewoptions& set viewoptions+=options set switchbuf= -" Clear Nvim default mappings. -mapclear -mapclear! - " Make "Q" switch to Ex mode. " This does not work for all tests. nnoremap Q gQ +" Common preparations for running tests. + +" Only load this once. +if exists('s:did_load') + finish +endif +let s:did_load = 1 + +" Clear Nvim default mappings. +mapclear +mapclear! + " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim index 450c6f98f5..272937387d 100644 --- a/src/nvim/testdir/test_arabic.vim +++ b/src/nvim/testdir/test_arabic.vim @@ -2,9 +2,8 @@ " NOTE: This just checks if the code works. If you know Arabic please add " functional tests that check the shaping works with real text. -if !has('arabic') - throw 'Skipped: arabic feature missing' -endif +source check.vim +CheckFeature arabic source view_util.vim @@ -563,3 +562,26 @@ func Test_shape_combination_isolated() set arabicshape& bwipe! endfunc + +" Test for entering arabic character in a search command +func Test_arabic_chars_in_search_cmd() + new + set arabic + call feedkeys("i\nsghl!\<C-^>vim\<C-^>", 'tx') + call cursor(1, 1) + call feedkeys("/^sghl!\<C-^>vim$\<C-^>\<CR>", 'tx') + call assert_equal([2, 1], [line('.'), col('.')]) + + " Try searching in left-to-right mode + set rightleftcmd= + call cursor(1, 1) + call feedkeys("/^sghl!\<C-^>vim$\<CR>", 'tx') + call assert_equal([2, 1], [line('.'), col('.')]) + + set rightleftcmd& + set rightleft& + set arabic& + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index 28c5948142..9d0d5b3e70 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -257,6 +257,26 @@ func Test_assert_with_msg() call remove(v:errors, 0) endfunc +func Test_mouse_position() + throw 'Skipped: Nvim does not have test_setmouse()' + let save_mouse = &mouse + set mouse=a + new + call setline(1, ['line one', 'line two']) + call assert_equal([0, 1, 1, 0], getpos('.')) + call test_setmouse(1, 5) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 5, 0], getpos('.')) + call test_setmouse(2, 20) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 2, 8, 0], getpos('.')) + call test_setmouse(5, 1) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 2, 1, 0], getpos('.')) + bwipe! + let &mouse = save_mouse +endfunc + " Must be last. func Test_zz_quit_detected() " Verify that if a test function ends Vim the test script detects this. diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index fcef57e47a..4b575bdfc3 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1720,7 +1720,7 @@ func Test_Cmd_Autocmds() au BufWriteCmd XtestE call extend(g:lines, getline(0, '$')) wall " will write other window to 'lines' call assert_equal(4, len(g:lines), g:lines) - call assert_equal("\tasdf", g:lines[2]) + call assert_equal("asdf", g:lines[2]) au! BufReadCmd au! BufWriteCmd diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index b00764e0dd..93b5e4d1cf 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -2,6 +2,7 @@ source check.vim source screendump.vim +source view_util.vim func Test_complete_tab() call writefile(['testfile'], 'Xtestfile') @@ -18,6 +19,11 @@ func Test_complete_list() " We can't see the output, but at least we check the code runs properly. call feedkeys(":e test\<C-D>\r", "tx") call assert_equal('test', expand('%:t')) + + " If a command doesn't support completion, then CTRL-D should be literally + " used. + call feedkeys(":chistory \<C-D>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"chistory \<C-D>", @:) endfunc func Test_complete_wildmenu() @@ -71,6 +77,11 @@ func Test_complete_wildmenu() cunmap <C-K> endif + " Test for canceling the wild menu by adding a character + redrawstatus + call feedkeys(":e Xdir1/\<Tab>x\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xdir1/Xdir2/x', @:) + " Completion using a relative path cd Xdir1/Xdir2 call feedkeys(":e ../\<Tab>\<Right>\<Down>\<C-A>\<C-B>\"\<CR>", 'tx') @@ -588,8 +599,17 @@ func Test_cmdline_paste() endtry call assert_equal("Xtestfile", bufname("%")) - " Use an invalid expression for <C-\>e - call assert_beeps('call feedkeys(":\<C-\>einvalid\<CR>", "tx")') + " Try to paste an invalid register using <C-R> + call feedkeys(":\"one\<C-R>\<C-X>two\<CR>", 'xt') + call assert_equal('"onetwo', @:) + + let @a = "xy\<C-H>z" + call feedkeys(":\"\<C-R>a\<CR>", 'xt') + call assert_equal('"xz', @:) + call feedkeys(":\"\<C-R>\<C-O>a\<CR>", 'xt') + call assert_equal("\"xy\<C-H>z", @:) + + call assert_beeps('call feedkeys(":\<C-R>=\<C-R>=\<Esc>", "xt")') bwipe! endfunc @@ -787,6 +807,149 @@ func Test_cmdline_complete_expression() unlet g:SomeVar endfunc +" Test for various command-line completion +func Test_cmdline_complete_various() + " completion for a command starting with a comment + call feedkeys(": :|\"\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\" :|\"\<C-A>", @:) + + " completion for a range followed by a comment + call feedkeys(":1,2\"\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"1,2\"\<C-A>", @:) + + " completion for :k command + call feedkeys(":ka\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ka\<C-A>", @:) + + " completion for short version of the :s command + call feedkeys(":sI \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"sI \<C-A>", @:) + + " completion for :write command + call mkdir('Xdir') + call writefile(['one'], 'Xdir/Xfile1') + let save_cwd = getcwd() + cd Xdir + call feedkeys(":w >> \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"w >> Xfile1", @:) + call chdir(save_cwd) + call delete('Xdir', 'rf') + + " completion for :w ! and :r ! commands + call feedkeys(":w !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"w !invalid_xyz_cmd", @:) + call feedkeys(":r !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"r !invalid_xyz_cmd", @:) + + " completion for :>> and :<< commands + call feedkeys(":>>>\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\">>>\<C-A>", @:) + call feedkeys(":<<<\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"<<<\<C-A>", @:) + + " completion for command with +cmd argument + call feedkeys(":buffer +/pat Xabc\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"buffer +/pat Xabc", @:) + call feedkeys(":buffer +/pat\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"buffer +/pat\<C-A>", @:) + + " completion for a command with a trailing comment + call feedkeys(":ls \" comment\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ls \" comment\<C-A>", @:) + + " completion for a command with a trailing command + call feedkeys(":ls | ls\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ls | ls", @:) + + " completion for a command with an CTRL-V escaped argument + call feedkeys(":ls \<C-V>\<C-V>a\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ls \<C-V>a\<C-A>", @:) + + " completion for a command that doesn't take additional arguments + call feedkeys(":all abc\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"all abc\<C-A>", @:) + + " completion for a command with a command modifier + call feedkeys(":topleft new\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"topleft new", @:) + + " completion for the :match command + call feedkeys(":match Search /pat/\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"match Search /pat/\<C-A>", @:) + + " completion for the :s command + call feedkeys(":s/from/to/g\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/from/to/g\<C-A>", @:) + + " completion for the :dlist command + call feedkeys(":dlist 10 /pat/ a\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"dlist 10 /pat/ a\<C-A>", @:) + + " completion for the :doautocmd command + call feedkeys(":doautocmd User MyCmd a.c\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"doautocmd User MyCmd a.c\<C-A>", @:) + + " completion for the :augroup command + augroup XTest + augroup END + call feedkeys(":augroup X\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"augroup XTest", @:) + augroup! XTest + + " completion for the :unlet command + call feedkeys(":unlet one two\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"unlet one two", @:) + + " completion for the :bdelete command + call feedkeys(":bdel a b c\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"bdel a b c", @:) + + " completion for the :mapclear command + call feedkeys(":mapclear \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"mapclear <buffer>", @:) + + " completion for user defined commands with menu names + menu Test.foo :ls<CR> + com -nargs=* -complete=menu MyCmd + call feedkeys(":MyCmd Te\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd Test.', @:) + delcom MyCmd + unmenu Test + + " completion for user defined commands with mappings + mapclear + map <F3> :ls<CR> + com -nargs=* -complete=mapping MyCmd + call feedkeys(":MyCmd <F\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd <F3>', @:) + mapclear + delcom MyCmd + + " completion for :set path= with multiple backslashes + call feedkeys(":set path=a\\\\\\ b\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set path=a\\\ b', @:) + + " completion for :set dir= with a backslash + call feedkeys(":set dir=a\\ b\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set dir=a\ b', @:) + + " completion for the :py3 commands + call feedkeys(":py3\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"py3 py3do py3file', @:) + + " redir @" is not the start of a comment. So complete after that + call feedkeys(":redir @\" | cwin\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"redir @" | cwindow', @:) + + " completion after a backtick + call feedkeys(":e `a1b2c\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"e `a1b2c', @:) + + " completion for the expression register + call feedkeys(":\"\<C-R>=float2\t\"\<C-B>\"\<CR>", 'xt') + call assert_equal('"float2nr("', @=) +endfunc + func Test_cmdline_write_alternatefile() new call setline('.', ['one', 'two']) @@ -1226,6 +1389,164 @@ func Test_cmdline_expand_home() call delete('Xdir', 'rf') endfunc +" Test for using CTRL-\ CTRL-G in the command line to go back to normal mode +" or insert mode (when 'insertmode' is set) +func Test_cmdline_ctrl_g() + new + call setline(1, 'abc') + call cursor(1, 3) + " If command line is entered from insert mode, using C-\ C-G should back to + " insert mode + call feedkeys("i\<C-O>:\<C-\>\<C-G>xy", 'xt') + call assert_equal('abxyc', getline(1)) + call assert_equal(4, col('.')) + + " If command line is entered in 'insertmode', using C-\ C-G should back to + " 'insertmode' + " call feedkeys(":set im\<cr>\<C-L>:\<C-\>\<C-G>12\<C-L>:set noim\<cr>", 'xt') + " call assert_equal('ab12xyc', getline(1)) + close! +endfunc + +" Test for 'wildmode' +func Test_wildmode() + func T(a, c, p) + return "oneA\noneB\noneC" + endfunc + command -nargs=1 -complete=custom,T MyCmd + + func SaveScreenLine() + let g:Sline = Screenline(&lines - 1) + return '' + endfunc + cnoremap <expr> <F2> SaveScreenLine() + + set nowildmenu + set wildmode=full,list + let g:Sline = '' + call feedkeys(":MyCmd \t\t\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('oneA oneB oneC', g:Sline) + call assert_equal('"MyCmd oneA', @:) + + set wildmode=longest,full + call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd one', @:) + call feedkeys(":MyCmd o\t\t\t\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd oneC', @:) + + set wildmode=longest + call feedkeys(":MyCmd one\t\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd one', @:) + + set wildmode=list:longest + let g:Sline = '' + call feedkeys(":MyCmd \t\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('oneA oneB oneC', g:Sline) + call assert_equal('"MyCmd one', @:) + + set wildmode="" + call feedkeys(":MyCmd \t\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd oneA', @:) + + " Test for wildmode=longest with 'fileignorecase' set + set wildmode=longest + set fileignorecase + argadd AA AAA AAAA + call feedkeys(":buffer \t\<C-B>\"\<CR>", 'xt') + call assert_equal('"buffer AA', @:) + set fileignorecase& + + " Test for listing files with wildmode=list + set wildmode=list + let g:Sline = '' + call feedkeys(":b A\t\t\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('AA AAA AAAA', g:Sline) + call assert_equal('"b A', @:) + + %argdelete + delcommand MyCmd + delfunc T + delfunc SaveScreenLine + cunmap <F2> + set wildmode& + %bwipe! +endfunc + +" Test for interrupting the command-line completion +func Test_interrupt_compl() + func F(lead, cmdl, p) + if a:lead =~ 'tw' + call interrupt() + return + endif + return "one\ntwo\nthree" + endfunc + command -nargs=1 -complete=custom,F Tcmd + + set nowildmenu + set wildmode=full + let interrupted = 0 + try + call feedkeys(":Tcmd tw\<Tab>\<C-B>\"\<CR>", 'xt') + catch /^Vim:Interrupt$/ + let interrupted = 1 + endtry + call assert_equal(1, interrupted) + + delcommand Tcmd + delfunc F + set wildmode& +endfunc + +" Test for moving the cursor on the : command line +func Test_cmdline_edit() + let str = ":one two\<C-U>" + let str ..= "one two\<C-W>\<C-W>" + let str ..= "\<Left>five\<Right>" + let str ..= "\<Home>two " + let str ..= "\<C-Left>one " + let str ..= "\<C-Right> three" + let str ..= "\<End>\<S-Left>four " + let str ..= "\<S-Right> six" + let str ..= "\<C-B>\"\<C-E> seven\<CR>" + call feedkeys(str, 'xt') + call assert_equal("\"one two three four five six seven", @:) +endfunc + +" Test for moving the cursor on the / command line in 'rightleft' mode +func Test_cmdline_edit_rightleft() + CheckFeature rightleft + set rightleft + set rightleftcmd=search + let str = "/one two\<C-U>" + let str ..= "one two\<C-W>\<C-W>" + let str ..= "\<Right>five\<Left>" + let str ..= "\<Home>two " + let str ..= "\<C-Right>one " + let str ..= "\<C-Left> three" + let str ..= "\<End>\<S-Right>four " + let str ..= "\<S-Left> six" + let str ..= "\<C-B>\"\<C-E> seven\<CR>" + call assert_fails("call feedkeys(str, 'xt')", 'E486:') + call assert_equal("\"one two three four five six seven", @/) + set rightleftcmd& + set rightleft& +endfunc + +" Test for using <C-\>e in the command line to evaluate an expression +func Test_cmdline_expr() + " Evaluate an expression from the beginning of a command line + call feedkeys(":abc\<C-B>\<C-\>e\"\\\"hello\"\<CR>\<CR>", 'xt') + call assert_equal('"hello', @:) + + " Use an invalid expression for <C-\>e + call assert_beeps('call feedkeys(":\<C-\>einvalid\<CR>", "tx")') + + " Insert literal <CTRL-\> in the command line + call feedkeys(":\"e \<C-\>\<C-Y>\<CR>", 'xt') + call assert_equal("\"e \<C-\>\<C-Y>", @:) +endfunc + " Test for normal mode commands not supported in the cmd window func Test_cmdwin_blocked_commands() call assert_fails('call feedkeys("q:\<C-T>\<CR>", "xt")', 'E11:') diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim index c7dddf4164..55b230373f 100644 --- a/src/nvim/testdir/test_command_count.vim +++ b/src/nvim/testdir/test_command_count.vim @@ -33,7 +33,7 @@ func Test_command_count_0() delcommand RangeBuffers delcommand RangeBuffersAll - set nohidden + set hidden& set swapfile& endfunc diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim index faf37485cd..76ea35fa10 100644 --- a/src/nvim/testdir/test_cscope.vim +++ b/src/nvim/testdir/test_cscope.vim @@ -1,7 +1,11 @@ " Test for cscope commands. -if !has('cscope') || !executable('cscope') || !has('quickfix') - finish +source check.vim +CheckFeature cscope +CheckFeature quickfix + +if !executable('cscope') + throw 'Skipped: cscope program missing' endif func CscopeSetupOrClean(setup) diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index be9a77ee75..8c20c647f1 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -540,7 +540,7 @@ func Test_diffopt_hiddenoff() bwipe! bwipe! - set nohidden diffopt& + set hidden& diffopt& endfunc func Test_diffoff_hidden() @@ -577,7 +577,7 @@ func Test_diffoff_hidden() bwipe! bwipe! - set nohidden diffopt& + set hidden& diffopt& endfunc func Test_setting_cursor() diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index c3f7fb45f4..cff78620d1 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -64,6 +64,23 @@ func Test_ex_mode() let &encoding = encoding_save endfunc +" Test for :g/pat/visual to run vi commands in Ex mode +" This used to hang Vim before 8.2.0274. +func Test_Ex_global() + new + call setline(1, ['', 'foo', 'bar', 'foo', 'bar', 'foo']) + call feedkeys("Q\<bs>g/bar/visual\<CR>$rxQ$ryQvisual\<CR>j", "xt") + call assert_equal('bax', getline(3)) + call assert_equal('bay', getline(5)) + bwipe! +endfunc + +" In Ex-mode, a backslash escapes a newline +func Test_Ex_escape_enter() + call feedkeys("gQlet l = \"a\\\<kEnter>b\"\<cr>vi\<cr>", 'xt') + call assert_equal("a\rb", l) +endfunc + func Test_ex_mode_errors() " Not allowed to enter ex mode when text is locked au InsertCharPre <buffer> normal! gQ<CR> diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index b3052abb24..df2cf97633 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -147,7 +147,6 @@ endfunc " Test for the :insert command func Test_insert_cmd() - set noautoindent " test assumes noautoindent, but it's on by default in Nvim new call setline(1, [' L1']) call feedkeys(":insert\<CR> L2\<CR> L3\<CR>.\<CR>", 'xt') @@ -197,7 +196,6 @@ endfunc " Test for the :change command func Test_change_cmd() - set noautoindent " test assumes noautoindent, but it's on by default in Nvim new call setline(1, [' L1', 'L2', 'L3']) call feedkeys(":change\<CR> L4\<CR> L5\<CR>.\<CR>", 'xt') diff --git a/src/nvim/testdir/test_filetype_lua.vim b/src/nvim/testdir/test_filetype_lua.vim deleted file mode 100644 index d9c0dcba9c..0000000000 --- a/src/nvim/testdir/test_filetype_lua.vim +++ /dev/null @@ -1,3 +0,0 @@ -let g:do_filetype_lua = 1 -let g:did_load_filetypes = 0 -source test_filetype.vim diff --git a/src/nvim/testdir/test_float_func.vim b/src/nvim/testdir/test_float_func.vim index 1e0c75c49d..902a011a9d 100644 --- a/src/nvim/testdir/test_float_func.vim +++ b/src/nvim/testdir/test_float_func.vim @@ -1,8 +1,7 @@ " test float functions -if !has('float') - finish -end +source check.vim +CheckFeature float func Test_abs() call assert_equal('1.23', string(abs(1.23))) diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 2391b4a485..3feb4d5f7f 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1218,6 +1218,16 @@ func Test_input_func() call assert_fails("call input('F:', '', [])", 'E730:') endfunc +" Test for the inputdialog() function +func Test_inputdialog() + CheckNotGui + + call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt') + call assert_equal('xx', v) + call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt') + call assert_equal('yy', v) +endfunc + " Test for inputlist() func Test_inputlist() call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<cr>", 'tx') diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index 7cb9daf59a..403fc24391 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -137,7 +137,7 @@ func Test_gf_visual() bwipe! call delete('Xtest_gf_visual') - set nohidden + set hidden& endfunc func Test_gf_error() diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index 3b3dc17f44..96006ac7e7 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -1,8 +1,7 @@ " Tests for the history functions -if !has('cmdline_hist') - finish -endif +source check.vim +CheckFeature cmdline_hist set history=7 @@ -71,6 +70,14 @@ function History_Tests(hist) call assert_equal('', histget(a:hist, i)) call assert_equal('', histget(a:hist, i - 7 - 1)) endfor + + " Test for freeing an entry at the beginning of the history list + for i in range(1, 4) + call histadd(a:hist, 'text_' . i) + endfor + call histdel(a:hist, 1) + call assert_equal('', histget(a:hist, 1)) + call assert_equal('text_4', histget(a:hist, 4)) endfunction function Test_History() @@ -116,14 +123,14 @@ endfunc func Test_history_size() let save_histsz = &history call histdel(':') - set history=5 + set history=10 for i in range(1, 5) call histadd(':', 'cmd' .. i) endfor call assert_equal(5, histnr(':')) call assert_equal('cmd5', histget(':', -1)) - set history=10 + set history=15 for i in range(6, 10) call histadd(':', 'cmd' .. i) endfor @@ -138,6 +145,15 @@ func Test_history_size() call assert_equal('cmd7', histget(':', 7)) call assert_equal('abc', histget(':', -1)) + " This test works only when the language is English + if v:lang == "C" || v:lang =~ '^[Ee]n' + set history=0 + redir => v + call feedkeys(":history\<CR>", 'xt') + redir END + call assert_equal(["'history' option is zero"], split(v, "\n")) + endif + let &history=save_histsz endfunc @@ -159,4 +175,12 @@ func Test_history_search() delfunc SavePat endfunc +" Test for making sure the key value is not stored in history +func Test_history_crypt_key() + CheckFeature cryptv + call feedkeys(":set bs=2 key=abc ts=8\<CR>", 'xt') + call assert_equal('set bs=2 key= ts=8', histget(':')) + set key& bs& ts& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index c8104c6b73..e963f308a2 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -95,7 +95,7 @@ func Test_ins_complete() call delete('Xtest11.one') call delete('Xtest11.two') call delete('Xtestdata') - set cpt& cot& def& tags& tagbsearch& nohidden + set cpt& cot& def& tags& tagbsearch& hidden& cd .. call delete('Xdir', 'rf') endfunc diff --git a/src/nvim/testdir/test_join.vim b/src/nvim/testdir/test_join.vim index ecb969d10a..ac6ef8f29f 100644 --- a/src/nvim/testdir/test_join.vim +++ b/src/nvim/testdir/test_join.vim @@ -51,7 +51,7 @@ func Test_join_marks() /^This line/;'}-join call assert_equal([0, 4, 11, 0], getpos("'[")) - call assert_equal([0, 4, 66, 0], getpos("']")) + call assert_equal([0, 4, 67, 0], getpos("']")) enew! endfunc diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim index eca8a95564..4f831aa40b 100644 --- a/src/nvim/testdir/test_langmap.vim +++ b/src/nvim/testdir/test_langmap.vim @@ -1,5 +1,8 @@ " tests for 'langmap' +source check.vim +CheckFeature langmap + func Test_langmap() new set langmap=}l,^x,%v diff --git a/src/nvim/testdir/test_legacy_filetype.vim b/src/nvim/testdir/test_legacy_filetype.vim new file mode 100644 index 0000000000..772faaadb0 --- /dev/null +++ b/src/nvim/testdir/test_legacy_filetype.vim @@ -0,0 +1,4 @@ +let g:do_legacy_filetype = 1 +filetype on + +source test_filetype.vim diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim index 2fda12d8b4..affa0f96fa 100644 --- a/src/nvim/testdir/test_listlbr.vim +++ b/src/nvim/testdir/test_listlbr.vim @@ -2,9 +2,9 @@ scriptencoding latin1 -if !exists("+linebreak") || !has("conceal") - finish -endif +source check.vim +CheckOption linebreak +CheckFeature conceal source view_util.vim diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim index 1f100d6244..df1ed78119 100644 --- a/src/nvim/testdir/test_listlbr_utf8.vim +++ b/src/nvim/testdir/test_listlbr_utf8.vim @@ -3,9 +3,10 @@ set encoding=utf-8 scriptencoding utf-8 -if !exists("+linebreak") || !has("conceal") || !has("signs") - finish -endif +source check.vim +CheckOption linebreak +CheckFeature conceal +CheckFeature signs source view_util.vim diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim index 2b346e0720..c53c07d991 100644 --- a/src/nvim/testdir/test_makeencoding.vim +++ b/src/nvim/testdir/test_makeencoding.vim @@ -4,8 +4,7 @@ source shared.vim let s:python = PythonProg() if s:python == '' - " Can't run this test. - finish + throw 'Skipped: python program missing' endif let s:script = 'test_makeencoding.py' diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim index 1d0c740734..f33c7f694c 100644 --- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim +++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim @@ -1,7 +1,7 @@ " Test for matchadd() and conceal feature using utf-8. -if !has('conceal') - finish -endif + +source check.vim +CheckFeature conceal func s:screenline(lnum) abort let line = [] diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim index a2ef6e5c67..4af75be514 100644 --- a/src/nvim/testdir/test_menu.vim +++ b/src/nvim/testdir/test_menu.vim @@ -1,8 +1,7 @@ " Test that the system menu can be loaded. -if !has('menu') - finish -endif +source check.vim +CheckFeature menu func Test_load_menu() try diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index cf90e416c4..8ec408e62e 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -2,9 +2,8 @@ scriptencoding latin1 -if !has('mksession') - finish -endif +source check.vim +CheckFeature mksession source shared.vim source term_util.vim @@ -43,9 +42,9 @@ func Test_mksession() \ ' four leadinG spaces', \ 'two consecutive tabs', \ 'two tabs in one line', - \ 'one ä multibyteCharacter', - \ 'aä Ä two multiByte characters', - \ 'Aäöü three mulTibyte characters', + \ 'one ä multibyteCharacter', + \ 'aä Ä two multiByte characters', + \ 'Aäöü three mulTibyte characters', \ 'short line', \ ]) let tmpfile = 'Xtemp' diff --git a/src/nvim/testdir/test_mksession_utf8.vim b/src/nvim/testdir/test_mksession_utf8.vim index 722fd28beb..4e593cc21a 100644 --- a/src/nvim/testdir/test_mksession_utf8.vim +++ b/src/nvim/testdir/test_mksession_utf8.vim @@ -3,9 +3,8 @@ set encoding=utf-8 scriptencoding utf-8 -if !has('mksession') - finish -endif +source check.vim +CheckFeature mksession func Test_mksession_utf8() tabnew diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 2a3d977254..7b6cfa6bb4 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -630,7 +630,7 @@ func Test_copy_winopt() call assert_equal(4,&numberwidth) bw! - set nohidden + set hidden& endfunc func Test_shortmess_F() @@ -752,6 +752,29 @@ func Test_shellquote() call assert_match(': "#echo Hello#"', v) endfunc +" Test for the 'rightleftcmd' option +func Test_rightleftcmd() + CheckFeature rightleft + set rightleft + set rightleftcmd + + let g:l = [] + func AddPos() + call add(g:l, screencol()) + return '' + endfunc + cmap <expr> <F2> AddPos() + + call feedkeys("/\<F2>abc\<Left>\<F2>\<Right>\<Right>\<F2>" .. + \ "\<Left>\<F2>\<Esc>", 'xt') + call assert_equal([&co - 1, &co - 4, &co - 2, &co - 3], g:l) + + cunmap <F2> + unlet g:l + set rightleftcmd& + set rightleft& +endfunc + " Test for setting option values using v:false and v:true func Test_opt_boolean() set number& diff --git a/src/nvim/testdir/test_perl.vim b/src/nvim/testdir/test_perl.vim index b911a982f9..558d0a5d6b 100644 --- a/src/nvim/testdir/test_perl.vim +++ b/src/nvim/testdir/test_perl.vim @@ -1,8 +1,8 @@ " Tests for Perl interface -if !has('perl') || has('win32') - finish -endif +source check.vim +CheckFeature perl +CheckNotMSWindows " FIXME: RunTest don't see any error when Perl abort... perl $SIG{__WARN__} = sub { die "Unexpected warnings from perl: @_" }; diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim index ae8bc57c7f..745b7da086 100644 --- a/src/nvim/testdir/test_python2.vim +++ b/src/nvim/testdir/test_python2.vim @@ -1,9 +1,8 @@ " Test for python 2 commands. " TODO: move tests from test86.in here. -if !has('python') - finish -endif +source check.vim +CheckFeature python func Test_pydo() " Check deleting lines does not trigger ml_get error. diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim index f6a1942e24..69f5f6dcc0 100644 --- a/src/nvim/testdir/test_python3.vim +++ b/src/nvim/testdir/test_python3.vim @@ -1,9 +1,8 @@ " Test for python 3 commands. " TODO: move tests from test87.in here. -if !has('python3') - finish -endif +source check.vim +CheckFeature python3 func Test_py3do() " Check deleting lines does not trigger an ml_get error. diff --git a/src/nvim/testdir/test_pyx2.vim b/src/nvim/testdir/test_pyx2.vim index 6a8ebf3da0..eee825fa9b 100644 --- a/src/nvim/testdir/test_pyx2.vim +++ b/src/nvim/testdir/test_pyx2.vim @@ -1,8 +1,7 @@ " Test for pyx* commands and functions with Python 2. -if !has('python') - finish -endif +source check.vim +CheckFeature python set pyx=2 let s:py2pattern = '^2\.[0-7]\.\d\+' diff --git a/src/nvim/testdir/test_pyx3.vim b/src/nvim/testdir/test_pyx3.vim index 2044af3abe..db39f5134a 100644 --- a/src/nvim/testdir/test_pyx3.vim +++ b/src/nvim/testdir/test_pyx3.vim @@ -1,9 +1,8 @@ " Test for pyx* commands and functions with Python 3. set pyx=3 -if !has('python3') - finish -endif +source check.vim +CheckFeature python3 let s:py2pattern = '^2\.[0-7]\.\d\+' let s:py3pattern = '^3\.\d\+\.\d\+' diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim index 6e6f91362b..93865869fa 100644 --- a/src/nvim/testdir/test_quotestar.vim +++ b/src/nvim/testdir/test_quotestar.vim @@ -1,10 +1,9 @@ " *-register (quotestar) tests -if !has('clipboard') - finish -endif - source shared.vim +source check.vim + +CheckFeature clipboard_working func Do_test_quotestar_for_macunix() if empty(exepath('pbcopy')) || empty(exepath('pbpaste')) diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim index 37b9e783c6..b381f1ddbb 100644 --- a/src/nvim/testdir/test_reltime.vim +++ b/src/nvim/testdir/test_reltime.vim @@ -1,8 +1,8 @@ " Tests for reltime() -if !has('reltime') || !has('float') - finish -endif +source check.vim +CheckFeature reltime +CheckFeature float func Test_reltime() let now = reltime() diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim index 76d1306836..f6f430b04e 100644 --- a/src/nvim/testdir/test_sha256.vim +++ b/src/nvim/testdir/test_sha256.vim @@ -1,8 +1,8 @@ " Tests for the sha256() function. -if !has('cryptv') || !exists('*sha256') - finish -endif +source check.vim +CheckFeature cryptv +CheckFunction sha256 function Test_sha256() " test for empty string: diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim index 09baec0b7d..b8fe8422b3 100644 --- a/src/nvim/testdir/test_source.vim +++ b/src/nvim/testdir/test_source.vim @@ -46,3 +46,14 @@ func Test_source_sandbox() bwipe! call delete('Xsourcehello') endfunc + +" When deleting a file and immediately creating a new one the inode may be +" recycled. Vim should not recognize it as the same script. +func Test_different_script() + call writefile(['let s:var = "asdf"'], 'XoneScript') + source XoneScript + call delete('XoneScript') + call writefile(['let g:var = s:var'], 'XtwoScript') + call assert_fails('source XtwoScript', 'E121:') + call delete('XtwoScript') +endfunc diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index 5099818384..58f0760f48 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -2,9 +2,7 @@ " Note: this file uses latin1 encoding, but is used with utf-8 encoding. source check.vim -if !has('spell') - finish -endif +CheckFeature spell source screendump.vim diff --git a/src/nvim/testdir/test_spellfile.vim b/src/nvim/testdir/test_spellfile.vim index f1331a9f2a..b028e9d969 100644 --- a/src/nvim/testdir/test_spellfile.vim +++ b/src/nvim/testdir/test_spellfile.vim @@ -525,6 +525,13 @@ func Test_mkspell() call assert_fails('mkspell! Xtest.spl Xtest.dic', 'E17:') call delete('Xtest.spl', 'rf') + " can't write the .spl file as its directory does not exist + call writefile([], 'Xtest.aff') + call writefile([], 'Xtest.dic') + call assert_fails('mkspell DOES_NOT_EXIT/Xtest.spl Xtest.dic', 'E484:') + call delete('Xtest.aff') + call delete('Xtest.dic') + call assert_fails('mkspell en en_US abc_xyz', 'E755:') endfunc @@ -616,6 +623,11 @@ func Test_aff_file_format_error() let output = execute('mkspell! Xtest.spl Xtest') call assert_match('Wrong CHECKCOMPOUNDPATTERN value in Xtest.aff line 1: 0', output) + " Both compounding and NOBREAK specified + call writefile(['COMPOUNDFLAG c', 'NOBREAK'], 'Xtest.aff') + let output = execute('mkspell! Xtest.spl Xtest') + call assert_match('Warning: both compounding and NOBREAK specified', output) + " Duplicate affix entry in an affix file call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L Y 1', 'PFX L 0 re x'], \ 'Xtest.aff') @@ -733,36 +745,54 @@ func Test_spell_add_word() %bw! endfunc -" When 'spellfile' is not set, adding a new good word will automatically set -" the 'spellfile' -func Test_init_spellfile() - let save_rtp = &rtp - let save_encoding = &encoding - call mkdir('Xrtp/spell', 'p') - call writefile(['vim'], 'Xrtp/spell/Xtest.dic') - silent mkspell Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest.dic - set runtimepath=./Xrtp - set spelllang=Xtest +func Test_spellfile_verbose() + call writefile(['1', 'one'], 'XtestVerbose.dic') + call writefile([], 'XtestVerbose.aff') + mkspell! XtestVerbose-utf8.spl XtestVerbose set spell - silent spellgood abc - call assert_equal('./Xrtp/spell/Xtest.utf-8.add', &spellfile) - call assert_equal(['abc'], readfile('Xrtp/spell/Xtest.utf-8.add')) - call assert_true(filereadable('Xrtp/spell/Xtest.utf-8.spl')) - set spell& spelllang& spellfile& - call delete('Xrtp', 'rf') - let &encoding = save_encoding - let &rtp = save_rtp - %bw! + + " First time: the spl file should be read. + let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl') + call assert_match('Reading spell file "XtestVerbose-utf8.spl"', a) + + " Second time time: the spl file should not be read (already read). + let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl') + call assert_notmatch('Reading spell file "XtestVerbose-utf8.spl"', a) + + set spell& spelllang& + call delete('XtestVerbose.dic') + call delete('XtestVerbose.aff') + call delete('XtestVerbose-utf8.spl') endfunc -" Test for the 'mkspellmem' option -func Test_mkspellmem_opt() - call assert_fails('set mkspellmem=1000', 'E474:') - call assert_fails('set mkspellmem=1000,', 'E474:') - call assert_fails('set mkspellmem=1000,50', 'E474:') - call assert_fails('set mkspellmem=1000,50,', 'E474:') - call assert_fails('set mkspellmem=1000,50,10,', 'E474:') - call assert_fails('set mkspellmem=1000,50,0', 'E474:') +" Test NOBREAK (see :help spell-NOBREAK) +func Test_NOBREAK() + call writefile(['3', 'one', 'two', 'three' ], 'XtestNOBREAK.dic') + call writefile(['NOBREAK' ], 'XtestNOBREAK.aff') + + mkspell! XtestNOBREAK-utf8.spl XtestNOBREAK + set spell spelllang=XtestNOBREAK-utf8.spl + + call assert_equal(['', ''], spellbadword('One two three onetwo onetwothree threetwoone')) + + call assert_equal(['x', 'bad'], spellbadword('x')) + call assert_equal(['y', 'bad'], spellbadword('yone')) + call assert_equal(['z', 'bad'], spellbadword('onez')) + call assert_equal(['zero', 'bad'], spellbadword('Onetwozerothree')) + + new + call setline(1, 'Onetwwothree') + norm! fw1z= + call assert_equal('Onetwothree', getline(1)) + call setline(1, 'Onetwothre') + norm! fh1z= + call assert_equal('Onetwothree', getline(1)) + + bw! + set spell& spelllang& + call delete('XtestNOBREAK.dic') + call delete('XtestNOBREAK.aff') + call delete('XtestNOBREAK-utf8.spl') endfunc " Test CHECKCOMPOUNDPATTERN (see :help spell-CHECKCOMPOUNDPATTERN) @@ -777,7 +807,7 @@ func Test_spellfile_CHECKCOMPOUNDPATTERN() \ 'CHECKCOMPOUNDPATTERN wo on', \ 'COMPOUNDFLAG c'], 'XtestCHECKCOMPOUNDPATTERN.aff') - let output = execute('mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN') + mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN set spell spelllang=XtestCHECKCOMPOUNDPATTERN-utf8.spl " Check valid words with and without valid compounds. @@ -862,7 +892,7 @@ func Test_spellfile_COMMON() \ 'ted'], 'XtestCOMMON.dic') call writefile(['COMMON the and'], 'XtestCOMMON.aff') - let output = execute('mkspell! XtestCOMMON-utf8.spl XtestCOMMON') + mkspell! XtestCOMMON-utf8.spl XtestCOMMON set spell spelllang=XtestCOMMON-utf8.spl " COMMON words 'and' and 'the' should be the top suggestions. @@ -877,4 +907,121 @@ func Test_spellfile_COMMON() call delete('XtestCOMMON-utf8.spl') endfunc +" Test CIRCUMFIX (see: :help spell-CIRCUMFIX) +func Test_spellfile_CIRCUMFIX() + " Example taken verbatim from https://github.com/hunspell/hunspell/tree/master/tests + call writefile(['1', + \ 'nagy/C po:adj'], 'XtestCIRCUMFIX.dic') + call writefile(['# circumfixes: ~ obligate prefix/suffix combinations', + \ '# superlative in Hungarian: leg- (prefix) AND -bb (suffix)', + \ '', + \ 'CIRCUMFIX X', + \ '', + \ 'PFX A Y 1', + \ 'PFX A 0 leg/X .', + \ '', + \ 'PFX B Y 1', + \ 'PFX B 0 legesleg/X .', + \ '', + \ 'SFX C Y 3', + \ 'SFX C 0 obb . is:COMPARATIVE', + \ 'SFX C 0 obb/AX . is:SUPERLATIVE', + \ 'SFX C 0 obb/BX . is:SUPERSUPERLATIVE'], 'XtestCIRCUMFIX.aff') + + mkspell! XtestCIRCUMFIX-utf8.spl XtestCIRCUMFIX + set spell spelllang=XtestCIRCUMFIX-utf8.spl + + " From https://catalog.ldc.upenn.edu/docs/LDC2008T01/acta04.pdf: + " Hungarian English + " --------- ------- + " nagy great + " nagyobb greater + " legnagyobb greatest + " legeslegnagyob most greatest + call assert_equal(['', ''], spellbadword('nagy nagyobb legnagyobb legeslegnagyobb')) + + for badword in ['legnagy', 'legeslegnagy', 'legobb', 'legeslegobb'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + set spell& spelllang& + call delete('XtestCIRCUMFIX.dic') + call delete('XtestCIRCUMFIX.aff') + call delete('XtestCIRCUMFIX-utf8.spl') +endfunc + +" Test SFX that strips/chops characters +func Test_spellfile_SFX_strip() + " Simplified conjugation of Italian verbs ending in -are (first conjugation). + call writefile(['SFX A Y 4', + \ 'SFX A are iamo [^icg]are', + \ 'SFX A are hiamo [cg]are', + \ 'SFX A re mo iare', + \ 'SFX A re vamo are'], + \ 'XtestSFX.aff') + " Examples of Italian verbs: + " - cantare = to sing + " - cercare = to search + " - odiare = to hate + call writefile(['3', 'cantare/A', 'cercare/A', 'odiare/A'], 'XtestSFX.dic') + + mkspell! XtestSFX-utf8.spl XtestSFX + set spell spelllang=XtestSFX-utf8.spl + + " To sing, we're singing, we were singing. + call assert_equal(['', ''], spellbadword('cantare cantiamo cantavamo')) + + " To search, we're searching, we were searching. + call assert_equal(['', ''], spellbadword('cercare cerchiamo cercavamo')) + + " To hate, we hate, we were hating. + call assert_equal(['', ''], spellbadword('odiare odiamo odiavamo')) + + for badword in ['canthiamo', 'cerciamo', 'cantarevamo', 'odiiamo'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + call assert_equal(['cantiamo'], spellsuggest('canthiamo', 1)) + call assert_equal(['cerchiamo'], spellsuggest('cerciamo', 1)) + call assert_equal(['cantavamo'], spellsuggest('cantarevamo', 1)) + call assert_equal(['odiamo'], spellsuggest('odiiamo', 1)) + + set spell& spelllang& + call delete('XtestSFX.dic') + call delete('XtestSFX.aff') + call delete('XtestSFX-utf8.spl') +endfunc + +" When 'spellfile' is not set, adding a new good word will automatically set +" the 'spellfile' +func Test_init_spellfile() + let save_rtp = &rtp + let save_encoding = &encoding + call mkdir('Xrtp/spell', 'p') + call writefile(['vim'], 'Xrtp/spell/Xtest.dic') + silent mkspell Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest.dic + set runtimepath=./Xrtp + set spelllang=Xtest + set spell + silent spellgood abc + call assert_equal('./Xrtp/spell/Xtest.utf-8.add', &spellfile) + call assert_equal(['abc'], readfile('Xrtp/spell/Xtest.utf-8.add')) + call assert_true(filereadable('Xrtp/spell/Xtest.utf-8.spl')) + set spell& spelllang& spellfile& + call delete('Xrtp', 'rf') + let &encoding = save_encoding + let &rtp = save_rtp + %bw! +endfunc + +" Test for the 'mkspellmem' option +func Test_mkspellmem_opt() + call assert_fails('set mkspellmem=1000', 'E474:') + call assert_fails('set mkspellmem=1000,', 'E474:') + call assert_fails('set mkspellmem=1000,50', 'E474:') + call assert_fails('set mkspellmem=1000,50,', 'E474:') + call assert_fails('set mkspellmem=1000,50,10,', 'E474:') + call assert_fails('set mkspellmem=1000,50,0', 'E474:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index 8579457d9f..f36ff83fdf 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -9,6 +9,10 @@ source view_util.vim source check.vim source term_util.vim +func SetUp() + set laststatus=2 +endfunc + func s:get_statusline() return ScreenLines(&lines - 1, &columns)[0] endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 4d1a468e30..7ba0149971 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -1,5 +1,8 @@ " Test for syntax and syntax iskeyword option +source check.vim +CheckFeature syntax + source view_util.vim source screendump.vim diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim index e11815ff33..be46773256 100644 --- a/src/nvim/testdir/test_taglist.vim +++ b/src/nvim/testdir/test_taglist.vim @@ -1,5 +1,7 @@ " test taglist(), tagfiles() functions and :tags command +source view_util.vim + func Test_taglist() call writefile([ \ "FFoo\tXfoo\t1", @@ -222,3 +224,21 @@ func Test_format_error() set tags& call delete('Xtags') endfunc + +" Test for :tag command completion with 'wildoptions' set to 'tagfile' +func Test_tag_complete_wildoptions() + call writefile(["foo\ta.c\t10;\"\tf", "bar\tb.c\t20;\"\td"], 'Xtags') + set tags=Xtags + set wildoptions=tagfile + + call feedkeys(":tag \<C-D>\<C-R>=Screenline(&lines - 1)\<CR> : " + \ .. "\<C-R>=Screenline(&lines - 2)\<CR>\<C-B>\"\<CR>", 'xt') + + call assert_equal('"tag bar d b.c : foo f a.c', @:) + + call delete('Xtags') + set wildoptions& + set tags& +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim index 81f2fea026..210aba19a9 100644 --- a/src/nvim/testdir/test_textobjects.vim +++ b/src/nvim/testdir/test_textobjects.vim @@ -1,8 +1,7 @@ " Test for textobjects -if !has('textobjects') - finish -endif +source check.vim +CheckFeature textobjects func CpoM(line, useM, expected) new diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 5c67efb831..e5b4bc23e8 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -1,8 +1,7 @@ " Test for timers -if !has('timers') - finish -endif +source check.vim +CheckFeature timers source shared.vim source term_util.vim @@ -317,8 +316,8 @@ endfunc " Test that the garbage collector isn't triggered if a timer callback invokes " vgetc(). func Test_nocatch_garbage_collect() - CheckFunction test_garbagecollect_soon - CheckFunction test_override + " skipped: Nvim does not support test_garbagecollect_soon(), test_override() + return " 'uptimetime. must be bigger than the timer timeout set ut=200 call test_garbagecollect_soon() diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 015a16705e..e37fe43b22 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -313,7 +313,7 @@ func CustomComplete(A, L, P) endfunc func CustomCompleteList(A, L, P) - return [ "Monday", "Tuesday", "Wednesday" ] + return [ "Monday", "Tuesday", "Wednesday", {}] endfunc func Test_CmdCompletion() diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index 6af199a512..68fe15ff93 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -1,8 +1,7 @@ " Test for variable tabstops -if !has("vartabs") - finish -endif +source check.vim +CheckFeature vartabs source view_util.vim diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 0fe5fc59eb..d295e520c7 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -612,7 +612,7 @@ func Test_window_prevwin() " reset q call delete('tmp.txt') - set nohidden autoread&vim + set hidden&vim autoread&vim delfunc Fun_RenewFile endfunc diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 20cf1ed586..e2289eb9ce 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1642,6 +1642,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col bool xterm = terminfo_is_term_family(term, "xterm") // Treat Terminal.app as generic xterm-like, for now. || nsterm; + bool hterm = terminfo_is_term_family(term, "hterm"); bool kitty = terminfo_is_term_family(term, "xterm-kitty"); bool linuxvt = terminfo_is_term_family(term, "linux"); bool bsdvt = terminfo_is_bsd_console(term); @@ -1705,7 +1706,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col unibi_set_bool(ut, unibi_back_color_erase, false); } - if (xterm) { + if (xterm || hterm) { // Termit, LXTerminal, GTKTerm2, GNOME Terminal, MATE Terminal, roxterm, // and EvilVTE falsely claim to be xterm and do not support important xterm // control sequences that we use. In an ideal world, these would have @@ -1714,9 +1715,13 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col // treatable as xterm. // 2017-04 terminfo.src lacks these. Xterm-likes have them. - unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;"); - unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); - unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr"); + if (!hterm) { + // hterm doesn't have a status line. + unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;"); + unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); + // TODO(aktau): patch this in when DECSTBM is fixed (https://crbug.com/1298796) + unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr"); + } unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m"); unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m"); @@ -1727,6 +1732,9 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds"); } else { // Fix things advertised via TERM=xterm, for non-xterm. + // + // TODO(aktau): stop patching this out for hterm when it gains support + // (https://crbug.com/1175065). if (unibi_get_str(ut, unibi_set_lr_margin)) { ILOG("Disabling smglr with TERM=xterm for non-xterm."); unibi_set_str(ut, unibi_set_lr_margin, NULL); @@ -1875,6 +1883,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col && ((xterm && !vte_version) // anything claiming xterm compat // per MinTTY 0.4.3-1 release notes from 2009 || putty + // per https://chromium.googlesource.com/apps/libapps/+/a5fb83c190aa9d74f4a9bca233dac6be2664e9e9/hterm/doc/ControlSequences.md + || hterm // per https://bugzilla.gnome.org/show_bug.cgi?id=720821 || (vte_version >= 3900) || (konsolev >= 180770) // #9364 @@ -1959,6 +1969,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, bool xterm = terminfo_is_term_family(term, "xterm") // Treat Terminal.app as generic xterm-like, for now. || nsterm; + bool hterm = terminfo_is_term_family(term, "hterm"); bool bsdvt = terminfo_is_bsd_console(term); bool dtterm = terminfo_is_term_family(term, "dtterm"); bool rxvt = terminfo_is_term_family(term, "rxvt"); @@ -1988,7 +1999,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, "ext.resize_screen", "\x1b[8;%p1%d;%p2%dt"); } - if (putty || xterm || rxvt) { + if (putty || xterm || hterm || rxvt) { data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut, "ext.reset_scroll_region", "\x1b[r"); @@ -2045,7 +2056,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, // would use a tmux control sequence and an extra if(screen) test. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\")); - } else if ((xterm || rxvt || tmux || alacritty) + } else if ((xterm || hterm || rxvt || tmux || alacritty) && (vte_version == 0 || vte_version >= 3900)) { // Supported in urxvt, newer VTE. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color", diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index fa650d611b..13a40fcc53 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -19,6 +19,26 @@ describe(':source', function() clear() end) + it('sourcing a file that is deleted and recreated is consistent vim-patch:8.1.0151', function() + local test_file = 'Xfile.vim' + local other_file = 'Xfoobar' + local script = [[ + func Func() + endfunc + ]] + write_file(test_file, script) + command('source ' .. test_file) + os.remove(test_file) + write_file(test_file, script) + command('source ' .. test_file) + os.remove(test_file) + write_file(other_file, '') + write_file(test_file, script) + command('source ' .. test_file) + os.remove(other_file) + os.remove(test_file) + end) + it('current buffer', function() insert([[ let a = 2 |