diff options
67 files changed, 901 insertions, 351 deletions
diff --git a/.github/scripts/install_deps.sh b/.github/scripts/install_deps.sh index 2b8335f192..66f418eb10 100755 --- a/.github/scripts/install_deps.sh +++ b/.github/scripts/install_deps.sh @@ -31,11 +31,17 @@ if [[ $os == Linux ]]; then if [[ -n $TEST ]]; then sudo apt-get install -y locales-all cpanminus attr libattr1-dev gdb fswatch + + # Use default CC to avoid compilation problems when installing Python modules + CC=cc python3 -m pip -q install --user --upgrade pynvim fi elif [[ $os == Darwin ]]; then brew update --quiet brew install ninja if [[ -n $TEST ]]; then brew install cpanminus fswatch + + # Use default CC to avoid compilation problems when installing Python modules + CC=cc python3 -m pip -q install --user --upgrade --break-system-packages pynvim fi fi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 622304512e..d7af975748 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -134,10 +134,6 @@ jobs: - if: ${{ matrix.test != 'unittest' }} name: Set up interpreter packages run: | - # Use default CC to avoid compilation problems when installing Python modules. - echo "Install neovim module for Python." - CC=cc python3 -m pip -q install --user --upgrade pynvim - echo "Install neovim RubyGem." gem install --no-document --bindir "$BIN_DIR" --user-install --pre neovim diff --git a/runtime/compiler/bdf.vim b/runtime/compiler/bdf.vim index b062e847aa..7e08cdddaa 100644 --- a/runtime/compiler/bdf.vim +++ b/runtime/compiler/bdf.vim @@ -1,7 +1,8 @@ " Vim compiler file " Compiler: BDF to PCF Conversion " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2006-04-19 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 if exists("current_compiler") finish @@ -11,9 +12,12 @@ let current_compiler = "bdf" let s:cpo_save = &cpo set cpo-=C -setlocal makeprg=bdftopcf\ $* +if exists(":CompilerSet") != 2 " Older Vim always used :setlocal + command -nargs=* CompilerSet setlocal <args> +endif -setlocal errorformat=%ABDF\ %trror\ on\ line\ %l:\ %m, +CompilerSet makeprg=bdftopcf\ $* +CompilerSet errorformat=%ABDF\ %trror\ on\ line\ %l:\ %m, \%-Z%p^, \%Cbdftopcf:\ bdf\ input\\,\ %f\\,\ corrupt, \%-G%.%# diff --git a/runtime/compiler/mcs.vim b/runtime/compiler/mcs.vim index c606586870..94a1848d4a 100644 --- a/runtime/compiler/mcs.vim +++ b/runtime/compiler/mcs.vim @@ -1,8 +1,8 @@ " Vim compiler file -" Compiler: Mono C# Compiler -" Maintainer: Jarek Sobiecki <harijari@go2.pl> -" Last Updated By: Peter Collingbourne -" Latest Revision: 2012 Jul 19 +" Compiler: Mono C# Compiler +" Maintainer: Jarek Sobiecki <harijari@go2.pl> +" Contributors: Peter Collingbourne and Enno Nagel +" Last Change: 2024 Mar 29 if exists("current_compiler") finish @@ -12,7 +12,12 @@ let current_compiler = "mcs" let s:cpo_save = &cpo set cpo-=C -setlocal errorformat= +if exists(":CompilerSet") != 2 " Older Vim always used :setlocal + command -nargs=* CompilerSet setlocal <args> +endif + +CompilerSet makeprg=mcs +CompilerSet errorformat= \%D%.%#Project\ \"%f/%[%^/\"]%#\"%.%#, \%X%.%#Done\ building\ project\ \"%f/%[%^/\"]%#\"%.%#, \%-G%\\s%.%#, diff --git a/runtime/compiler/modelsim_vcom.vim b/runtime/compiler/modelsim_vcom.vim index 6aa1bde0b3..184b403996 100644 --- a/runtime/compiler/modelsim_vcom.vim +++ b/runtime/compiler/modelsim_vcom.vim @@ -1,7 +1,8 @@ " Vim Compiler File " Compiler: Modelsim Vcom " Maintainer: Paul Baleme <pbaleme@mail.com> -" Last Change: September 8, 2003 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 " Thanks to: allanherriman@hotmail.com if exists("current_compiler") @@ -13,8 +14,9 @@ if exists(":CompilerSet") != 2 " older Vim always used :setlocal command -nargs=* CompilerSet setlocal <args> endif -"setlocal errorformat=\*\*\ %tRROR:\ %f(%l):\ %m,%tRROR:\ %f(%l):\ %m,%tARNING\[%*[0-9]\]:\ %f(%l):\ %m,\*\*\ %tRROR:\ %m,%tRROR:\ %m,%tARNING\[%*[0-9]\]:\ %m +CompilerSet makeprg=vcom +"setlocal errorformat=\*\*\ %tRROR:\ %f(%l):\ %m,%tRROR:\ %f(%l):\ %m,%tARNING\[%*[0-9]\]:\ %f(%l):\ %m,\*\*\ %tRROR:\ %m,%tRROR:\ %m,%tARNING\[%*[0-9]\]:\ %m "setlocal errorformat=%tRROR:\ %f(%l):\ %m,%tARNING\[%*[0-9]\]:\ %m CompilerSet errorformat=\*\*\ %tRROR:\ %f(%l):\ %m,\*\*\ %tRROR:\ %m,\*\*\ %tARNING:\ %m,\*\*\ %tOTE:\ %m,%tRROR:\ %f(%l):\ %m,%tARNING\[%*[0-9]\]:\ %f(%l):\ %m,%tRROR:\ %m,%tARNING\[%*[0-9]\]:\ %m diff --git a/runtime/compiler/powershell.vim b/runtime/compiler/powershell.vim index 45d5ec2191..445a2f6d97 100644 --- a/runtime/compiler/powershell.vim +++ b/runtime/compiler/powershell.vim @@ -1,7 +1,8 @@ " Vim compiler file " Compiler: powershell " URL: https://github.com/PProvost/vim-ps1 -" Last Change: 2020 Mar 30 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 if exists("current_compiler") finish @@ -37,7 +38,7 @@ let g:ps1_efm_show_error_categories = get(g:, 'ps1_efm_show_error_categories', 0 " Use absolute path because powershell requires explicit relative paths " (./file.ps1 is okay, but # expands to file.ps1) -let &l:makeprg = g:ps1_makeprg_cmd .' %:p:S' +let makeprg = g:ps1_makeprg_cmd .. ' %:p:S' " Parse file, line, char from callstacks: " Write-Ouput : The term 'Write-Ouput' is not recognized as the name of a @@ -50,6 +51,8 @@ let &l:makeprg = g:ps1_makeprg_cmd .' %:p:S' " + CategoryInfo : ObjectNotFound: (Write-Ouput:String) [], CommandNotFoundException " + FullyQualifiedErrorId : CommandNotFoundException +execute 'CompilerSet makeprg=' .. escape(makeprg, ' ') + " Showing error in context with underlining. CompilerSet errorformat=%+G+%m " Error summary. diff --git a/runtime/compiler/tex.vim b/runtime/compiler/tex.vim index 65e15cf6e2..0925d17680 100644 --- a/runtime/compiler/tex.vim +++ b/runtime/compiler/tex.vim @@ -1,7 +1,8 @@ " Vim compiler file " Compiler: TeX " Maintainer: Artem Chuprina <ran@ran.pp.ru> -" Last Change: 2012 Apr 30 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 if exists("current_compiler") finish @@ -27,7 +28,8 @@ if exists('b:tex_ignore_makefile') || exists('g:tex_ignore_makefile') || else let current_compiler = "latex" endif - let &l:makeprg=current_compiler.' -interaction=nonstopmode' + let makeprg=current_compiler .. ' -interaction=nonstopmode' + execute 'CompilerSet makeprg=' .. escape(makeprg, ' ') else let current_compiler = 'make' endif diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index b9bc73e0b8..d967e2b313 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1640,12 +1640,14 @@ vim.on_key({fn}, {ns_id}) *vim.on_key()* Note: ~ • {fn} will be removed on error. • {fn} will not be cleared by |nvim_buf_clear_namespace()| - • {fn} will receive the keys after mappings have been evaluated Parameters: ~ - • {fn} (`fun(key: string)?`) Function invoked on every key press. - |i_CTRL-V| Passing in nil when {ns_id} is specified removes - the callback associated with namespace {ns_id}. + • {fn} (`fun(key: string, typed: string)?`) Function invoked on + every key press. |i_CTRL-V| {key} is the key after mappings + have been applied, and {typed} is the key(s) before mappings + are applied, which may be empty if {key} is produced by + non-typed keys. When {fn} is nil and {ns_id} is specified, + the callback associated with namespace {ns_id} is removed. • {ns_id} (`integer?`) Namespace ID. If nil or 0, generates and returns a new |nvim_create_namespace()| id. @@ -2954,13 +2956,14 @@ vim.fs.joinpath({...}) *vim.fs.joinpath()* vim.fs.normalize({path}, {opts}) *vim.fs.normalize()* Normalize a path to a standard format. A tilde (~) character at the - beginning of the path is expanded to the user's home directory and any - backslash (\) characters are converted to forward slashes (/). Environment - variables are also expanded. + beginning of the path is expanded to the user's home directory and + environment variables are also expanded. + + On Windows, backslash (\) characters are converted to forward slashes (/). Examples: >lua vim.fs.normalize('C:\\\\Users\\\\jdoe') - -- 'C:/Users/jdoe' + -- On Windows: 'C:/Users/jdoe' vim.fs.normalize('~/src/neovim') -- '/home/jdoe/src/neovim' diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 1aee5c656b..e39cf95da9 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -354,6 +354,9 @@ The following changes to existing APIs or features add new behavior. • |vim.region()| can use a string accepted by |getpos()| as position. +• |vim.on_key()| callbacks receive a second argument for keys typed before + mappings are applied. + • |vim.diagnostic.config()| now accepts a function for the virtual_text.prefix option, which allows for rendering e.g., diagnostic severities differently. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index b0583d306d..54333ee85d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5693,8 +5693,7 @@ A jump table for the options with a short description can be found at |Q_op|. highlighted with |hl-NonText|. You may also want to add "lastline" to the 'display' option to show as much of the last line as possible. - NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y, - CTRL-D, CTRL-U, CTRL-F, CTRL-B and scrolling with the mouse. + NOTE: partly implemented, doesn't work yet for |gj| and |gk|. *'softtabstop'* *'sts'* 'softtabstop' 'sts' number (default 0) diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index b8182347f8..a39f4bc5d7 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -185,7 +185,7 @@ See 'clipboard' for details and options. The presence of a working clipboard tool implicitly enables the '+' and "*" registers. Nvim looks for these clipboard tools, in order of priority: - - |g:clipboard| + - |g:clipboard| (unless unset or `false`) - pbcopy, pbpaste (macOS) - wl-copy, wl-paste (if $WAYLAND_DISPLAY is set) - waycopy, waypaste (if $WAYLAND_DISPLAY is set) diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 4b99fbface..5e95e00e45 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4776,11 +4776,11 @@ guisp={color-name} *guisp* Colors which define Nvim's default color scheme: NvimDarkBlue NvimLightBlue NvimDarkCyan NvimLightCyan + NvimDarkGray1 NvimLightGray1 + NvimDarkGray2 NvimLightGray2 + NvimDarkGray3 NvimLightGray3 + NvimDarkGray4 NvimLightGray4 NvimDarkGreen NvimLightGreen - NvimDarkGrey1 NvimLightGrey1 - NvimDarkGrey2 NvimLightGrey2 - NvimDarkGrey3 NvimLightGrey3 - NvimDarkGrey4 NvimLightGrey4 NvimDarkMagenta NvimLightMagenta NvimDarkRed NvimLightRed NvimDarkYellow NvimLightYellow diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index f527fc194c..18f6cfa4ba 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -654,11 +654,14 @@ local on_key_cbs = {} --- @type table<integer,function> --- ---@note {fn} will be removed on error. ---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| ----@note {fn} will receive the keys after mappings have been evaluated --- ----@param fn fun(key: string)? Function invoked on every key press. |i_CTRL-V| ---- Passing in nil when {ns_id} is specified removes the ---- callback associated with namespace {ns_id}. +---@param fn fun(key: string, typed: string)? +--- Function invoked on every key press. |i_CTRL-V| +--- {key} is the key after mappings have been applied, and +--- {typed} is the key(s) before mappings are applied, which +--- may be empty if {key} is produced by non-typed keys. +--- When {fn} is nil and {ns_id} is specified, the callback +--- associated with namespace {ns_id} is removed. ---@param ns_id integer? Namespace ID. If nil or 0, generates and returns a --- new |nvim_create_namespace()| id. --- @@ -684,11 +687,11 @@ end --- Executes the on_key callbacks. ---@private -function vim._on_key(char) +function vim._on_key(buf, typed_buf) local failed_ns_ids = {} local failed_messages = {} for k, v in pairs(on_key_cbs) do - local ok, err_msg = pcall(v, char) + local ok, err_msg = pcall(v, buf, typed_buf) if not ok then vim.on_key(nil, k) table.insert(failed_ns_ids, k) diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index adc42f9659..5e40851457 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -6075,8 +6075,7 @@ vim.go.sta = vim.go.smarttab --- highlighted with `hl-NonText`. --- You may also want to add "lastline" to the 'display' option to show as --- much of the last line as possible. ---- NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y, ---- CTRL-D, CTRL-U, CTRL-F, CTRL-B and scrolling with the mouse. +--- NOTE: partly implemented, doesn't work yet for `gj` and `gk`. --- --- @type boolean vim.o.smoothscroll = false diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index f9fe122f01..ad0d914ea2 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -1,6 +1,7 @@ local M = {} local iswin = vim.uv.os_uname().sysname == 'Windows_NT' +local os_sep = iswin and '\\' or '/' --- Iterate over all the parents of the given path. --- @@ -47,19 +48,23 @@ function M.dirname(file) return nil end vim.validate({ file = { file, 's' } }) - if iswin and file:match('^%w:[\\/]?$') then - return (file:gsub('\\', '/')) - elseif not file:match('[\\/]') then + if iswin then + file = file:gsub(os_sep, '/') --[[@as string]] + if file:match('^%w:/?$') then + return file + end + end + if not file:match('/') then return '.' elseif file == '/' or file:match('^/[^/]+$') then return '/' end ---@type string - local dir = file:match('[/\\]$') and file:sub(1, #file - 1) or file:match('^([/\\]?.+)[/\\]') + local dir = file:match('/$') and file:sub(1, #file - 1) or file:match('^(/?.+)/') if iswin and dir:match('^%w:$') then return dir .. '/' end - return (dir:gsub('\\', '/')) + return dir end --- Return the basename of the given path @@ -72,10 +77,13 @@ function M.basename(file) return nil end vim.validate({ file = { file, 's' } }) - if iswin and file:match('^%w:[\\/]?$') then - return '' + if iswin then + file = file:gsub(os_sep, '/') --[[@as string]] + if file:match('^%w:/?$') then + return '' + end end - return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) + return file:match('/$') and '' or (file:match('[^/]*$')) end --- Concatenate directories and/or file paths into a single path with normalization @@ -334,15 +342,16 @@ end --- @field expand_env boolean --- Normalize a path to a standard format. A tilde (~) character at the ---- beginning of the path is expanded to the user's home directory and any ---- backslash (\) characters are converted to forward slashes (/). Environment ---- variables are also expanded. +--- beginning of the path is expanded to the user's home directory and +--- environment variables are also expanded. +--- +--- On Windows, backslash (\) characters are converted to forward slashes (/). --- --- Examples: --- --- ```lua --- vim.fs.normalize('C:\\\\Users\\\\jdoe') ---- -- 'C:/Users/jdoe' +--- -- On Windows: 'C:/Users/jdoe' --- --- vim.fs.normalize('~/src/neovim') --- -- '/home/jdoe/src/neovim' @@ -362,23 +371,41 @@ function M.normalize(path, opts) expand_env = { opts.expand_env, { 'boolean' }, true }, }) - if path:sub(1, 1) == '~' then + -- Expand ~ to users home directory + if vim.startswith(path, '~') then local home = vim.uv.os_homedir() or '~' - if home:sub(-1) == '\\' or home:sub(-1) == '/' then + if home:sub(-1) == os_sep then home = home:sub(1, -2) end path = home .. path:sub(2) end + -- Expand environment variables if `opts.expand_env` isn't `false` if opts.expand_env == nil or opts.expand_env then path = path:gsub('%$([%w_]+)', vim.uv.os_getenv) end - path = path:gsub('\\', '/'):gsub('/+', '/') + -- Convert path separator to `/` + path = path:gsub(os_sep, '/') + + -- Don't modify leading double slash as those have implementation-defined behavior according to + -- POSIX. They are also valid UNC paths. Three or more leading slashes are however collapsed to + -- a single slash. + if vim.startswith(path, '//') and not vim.startswith(path, '///') then + path = '/' .. path:gsub('/+', '/') + else + path = path:gsub('/+', '/') + end + + -- Ensure last slash is not truncated from root drive on Windows if iswin and path:match('^%w:/$') then return path end - return (path:gsub('(.)/$', '%1')) + + -- Remove trailing slashes + path = path:gsub('(.)/$', '%1') + + return path end return M diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 50121f30b2..43f52b8116 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -461,7 +461,14 @@ local function call_hierarchy(method) vim.notify(err.message, vim.log.levels.WARN) return end + if not result then + vim.notify('No item resolved', vim.log.levels.WARN) + return + end local call_hierarchy_item = pick_call_hierarchy_item(result) + if not call_hierarchy_item then + return + end local client = vim.lsp.get_client_by_id(ctx.client_id) if client then client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr) diff --git a/runtime/syntax/pamconf.vim b/runtime/syntax/pamconf.vim index 29132848a9..1b5f901348 100644 --- a/runtime/syntax/pamconf.vim +++ b/runtime/syntax/pamconf.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: pam(8) configuration file " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2020/08/04 +" Latest Change: 2024/03/31 " Changes By: Haochen Tong - +" Vim Project for the @include syntax if exists("b:current_syntax") finish @@ -23,6 +23,13 @@ syn match pamconfType '-\?[[:alpha:]]\+' syn keyword pamconfTypeKeyword contained account auth password session +" The @include syntax is Debian specific +syn match pamconfInclude '^@include' + \ nextgroup=pamconfIncludeFile + \ skipwhite + +syn match pamconfIncludeFile '\f\+$' + if s:has_service_field syn match pamconfService '^[[:graph:]]\+' \ nextgroup=pamconfType, @@ -124,6 +131,8 @@ hi def link pamconfMPath String hi def link pamconfMPathLineCont pamconfServiceLineCont hi def link pamconfArgs Normal hi def link pamconfArgsLineCont pamconfServiceLineCont +hi def link pamconfInclude Include +hi def link pamconfIncludeFile Include let b:current_syntax = "pamconf" diff --git a/runtime/syntax/sshconfig.vim b/runtime/syntax/sshconfig.vim index 750289d83e..99e10fd1ee 100644 --- a/runtime/syntax/sshconfig.vim +++ b/runtime/syntax/sshconfig.vim @@ -63,8 +63,7 @@ syn keyword sshconfigMAC hmac-sha2-256 syn keyword sshconfigMAC hmac-sha2-512 syn keyword sshconfigMAC hmac-md5 syn keyword sshconfigMAC hmac-md5-96 -syn keyword sshconfigMAC hmac-ripemd160 -syn match sshconfigMAC "\<hmac-ripemd160@openssh\.com\>" +syn match sshconfigMAC "\<hmac-ripemd160\%(@openssh\.com\)\?\>" syn match sshconfigMAC "\<umac-64@openssh\.com\>" syn match sshconfigMAC "\<umac-128@openssh\.com\>" syn match sshconfigMAC "\<hmac-sha1-etm@openssh\.com\>" @@ -107,33 +106,35 @@ syn keyword sshconfigSysLogFacility DAEMON USER AUTH AUTHPRIV LOCAL0 LOCAL1 syn keyword sshconfigSysLogFacility LOCAL2 LOCAL3 LOCAL4 LOCAL5 LOCAL6 LOCAL7 syn keyword sshconfigAddressFamily inet inet6 -syn match sshconfigIPQoS "af1[123]" -syn match sshconfigIPQoS "af2[123]" -syn match sshconfigIPQoS "af3[123]" -syn match sshconfigIPQoS "af4[123]" -syn match sshconfigIPQoS "cs[0-7]" -syn keyword sshconfigIPQoS ef lowdelay throughput reliability +syn match sshconfigIPQoS "\<af[1-4][1-3]\>" +syn match sshconfigIPQoS "\<cs[0-7]\>" +syn keyword sshconfigIPQoS ef le lowdelay throughput reliability syn keyword sshconfigKbdInteractive bsdauth pam skey syn keyword sshconfigKexAlgo diffie-hellman-group1-sha1 syn keyword sshconfigKexAlgo diffie-hellman-group14-sha1 syn keyword sshconfigKexAlgo diffie-hellman-group-exchange-sha1 syn keyword sshconfigKexAlgo diffie-hellman-group-exchange-sha256 +syn keyword sshconfigKexAlgo diffie-hellman-group16-sha512 +syn keyword sshconfigKexAlgo diffie-hellman-group18-sha512 +syn keyword sshconfigKexAlgo diffie-hellman-group14-sha256 syn keyword sshconfigKexAlgo ecdh-sha2-nistp256 syn keyword sshconfigKexAlgo ecdh-sha2-nistp384 syn keyword sshconfigKexAlgo ecdh-sha2-nistp521 -syn match sshconfigKexAlgo "\<curve25519-sha256@libssh\.org\>" +syn match sshconfigKexAlgo "\<curve25519-sha256\%(@libssh\.org\)\?\>" +syn match sshconfigKexAlgo "\<sntrup761x25519-sha512@openssh\.com\>" syn keyword sshconfigTunnel point-to-point ethernet -syn match sshconfigVar "%[rhplLdun]\>" +syn match sshconfigVar "%[CdfHhIijKkLlnprTtu]\>" +syn match sshconfigVar "%%" syn match sshconfigSpecial "[*?]" -syn match sshconfigNumber "\d\+" +syn match sshconfigNumber "\<\d\+\>" syn match sshconfigHostPort "\<\(\d\{1,3}\.\)\{3}\d\{1,3}\(:\d\+\)\?\>" syn match sshconfigHostPort "\<\([-a-zA-Z0-9]\+\.\)\+[-a-zA-Z0-9]\{2,}\(:\d\+\)\?\>" syn match sshconfigHostPort "\<\(\x\{,4}:\)\+\x\{,4}[:/]\d\+\>" -syn match sshconfigHostPort "\(Host \)\@<=.\+" -syn match sshconfigHostPort "\(HostName \)\@<=.\+" +syn match sshconfigHostPort "\<\c\(Host \+\)\@<=.\+" +syn match sshconfigHostPort "\<\c\(Hostname \+\)\@<=.\+" " case off syn case ignore @@ -142,10 +143,10 @@ syn case ignore " Keywords syn keyword sshconfigHostSect Host -syn keyword sshconfigMatch canonical final exec host originalhost user localuser all +syn keyword sshconfigMatch canonical final exec localnetwork host originalhost tagged user localuser all -syn keyword sshconfigKeyword AddressFamily syn keyword sshconfigKeyword AddKeysToAgent +syn keyword sshconfigKeyword AddressFamily syn keyword sshconfigKeyword BatchMode syn keyword sshconfigKeyword BindAddress syn keyword sshconfigKeyword BindInterface @@ -157,16 +158,18 @@ syn keyword sshconfigKeyword CanonicalizePermittedCNAMEs syn keyword sshconfigKeyword CASignatureAlgorithms syn keyword sshconfigKeyword CertificateFile syn keyword sshconfigKeyword ChallengeResponseAuthentication +syn keyword sshconfigKeyword ChannelTimeout syn keyword sshconfigKeyword CheckHostIP syn keyword sshconfigKeyword Ciphers syn keyword sshconfigKeyword ClearAllForwardings syn keyword sshconfigKeyword Compression -syn keyword sshconfigKeyword ConnectTimeout syn keyword sshconfigKeyword ConnectionAttempts +syn keyword sshconfigKeyword ConnectTimeout syn keyword sshconfigKeyword ControlMaster syn keyword sshconfigKeyword ControlPath syn keyword sshconfigKeyword ControlPersist syn keyword sshconfigKeyword DynamicForward +syn keyword sshconfigKeyword EnableEscapeCommandline syn keyword sshconfigKeyword EnableSSHKeysign syn keyword sshconfigKeyword EscapeChar syn keyword sshconfigKeyword ExitOnForwardFailure @@ -176,18 +179,17 @@ syn keyword sshconfigKeyword ForwardAgent syn keyword sshconfigKeyword ForwardX11 syn keyword sshconfigKeyword ForwardX11Timeout syn keyword sshconfigKeyword ForwardX11Trusted -syn keyword sshconfigKeyword GSSAPIAuthentication -syn keyword sshconfigKeyword GSSAPIDelegateCredentials syn keyword sshconfigKeyword GatewayPorts syn keyword sshconfigKeyword GlobalKnownHostsFile +syn keyword sshconfigKeyword GSSAPIAuthentication +syn keyword sshconfigKeyword GSSAPIDelegateCredentials syn keyword sshconfigKeyword HashKnownHosts -syn keyword sshconfigKeyword HostKeyAlgorithms -syn keyword sshconfigKeyword HostKeyAlias -syn keyword sshconfigKeyword HostName -syn keyword sshconfigKeyword HostbasedAuthentication syn keyword sshconfigKeyword HostbasedAcceptedAlgorithms +syn keyword sshconfigKeyword HostbasedAuthentication syn keyword sshconfigKeyword HostbasedKeyTypes -syn keyword sshconfigKeyword IPQoS +syn keyword sshconfigKeyword HostKeyAlgorithms +syn keyword sshconfigKeyword HostKeyAlias +syn keyword sshconfigKeyword Hostname syn keyword sshconfigKeyword IdentitiesOnly syn keyword sshconfigKeyword IdentityAgent syn keyword sshconfigKeyword IdentityFile @@ -206,15 +208,16 @@ syn keyword sshconfigKeyword MACs syn keyword sshconfigKeyword Match syn keyword sshconfigKeyword NoHostAuthenticationForLocalhost syn keyword sshconfigKeyword NumberOfPasswordPrompts -syn keyword sshconfigKeyword PKCS11Provider +syn keyword sshconfigKeyword ObscureKeystrokeTiming syn keyword sshconfigKeyword PasswordAuthentication syn keyword sshconfigKeyword PermitLocalCommand syn keyword sshconfigKeyword PermitRemoteOpen +syn keyword sshconfigKeyword PKCS11Provider syn keyword sshconfigKeyword Port syn keyword sshconfigKeyword PreferredAuthentications syn keyword sshconfigKeyword ProxyCommand syn keyword sshconfigKeyword ProxyJump -syn keyword sshconfigKeyword ProxyUseFDPass +syn keyword sshconfigKeyword ProxyUseFdpass syn keyword sshconfigKeyword PubkeyAcceptedAlgorithms syn keyword sshconfigKeyword PubkeyAcceptedKeyTypes syn keyword sshconfigKeyword PubkeyAuthentication @@ -229,18 +232,19 @@ syn keyword sshconfigKeyword SendEnv syn keyword sshconfigKeyword ServerAliveCountMax syn keyword sshconfigKeyword ServerAliveInterval syn keyword sshconfigKeyword SessionType -syn keyword sshconfigKeyword SmartcardDevice syn keyword sshconfigKeyword SetEnv +syn keyword sshconfigKeyword SmartcardDevice syn keyword sshconfigKeyword StdinNull syn keyword sshconfigKeyword StreamLocalBindMask syn keyword sshconfigKeyword StreamLocalBindUnlink syn keyword sshconfigKeyword StrictHostKeyChecking syn keyword sshconfigKeyword SyslogFacility +syn keyword sshconfigKeyword Tag syn keyword sshconfigKeyword TCPKeepAlive syn keyword sshconfigKeyword Tunnel syn keyword sshconfigKeyword TunnelDevice -syn keyword sshconfigKeyword UseBlacklistedKeys syn keyword sshconfigKeyword UpdateHostKeys +syn keyword sshconfigKeyword UseBlacklistedKeys syn keyword sshconfigKeyword User syn keyword sshconfigKeyword UserKnownHostsFile syn keyword sshconfigKeyword VerifyHostKeyDNS @@ -268,9 +272,9 @@ syn keyword sshconfigDeprecated UsePrivilegedPort hi def link sshconfigComment Comment hi def link sshconfigTodo Todo hi def link sshconfigHostPort sshconfigConstant -hi def link sshconfigNumber sshconfigConstant +hi def link sshconfigNumber Number hi def link sshconfigConstant Constant -hi def link sshconfigYesNo sshconfigEnum +hi def link sshconfigYesNo Boolean hi def link sshconfigCipher sshconfigDeprecated hi def link sshconfigCiphers sshconfigEnum hi def link sshconfigMAC sshconfigEnum diff --git a/runtime/syntax/sshdconfig.vim b/runtime/syntax/sshdconfig.vim index c0d9c3f598..8b539d907b 100644 --- a/runtime/syntax/sshdconfig.vim +++ b/runtime/syntax/sshdconfig.vim @@ -64,8 +64,7 @@ syn keyword sshdconfigMAC hmac-sha2-256 syn keyword sshdconfigMAC hmac-sha2-512 syn keyword sshdconfigMAC hmac-md5 syn keyword sshdconfigMAC hmac-md5-96 -syn keyword sshdconfigMAC hmac-ripemd160 -syn match sshdconfigMAC "\<hmac-ripemd160@openssh\.com\>" +syn match sshdconfigMAC "\<hmac-ripemd160\%(@openssh\.com\)\?\>" syn match sshdconfigMAC "\<umac-64@openssh\.com\>" syn match sshdconfigMAC "\<umac-128@openssh\.com\>" syn match sshdconfigMAC "\<hmac-sha1-etm@openssh\.com\>" @@ -108,12 +107,9 @@ syn keyword sshdconfigSysLogFacility LOCAL2 LOCAL3 LOCAL4 LOCAL5 LOCAL6 LOCAL7 syn keyword sshdconfigCompression delayed -syn match sshdconfigIPQoS "af1[123]" -syn match sshdconfigIPQoS "af2[123]" -syn match sshdconfigIPQoS "af3[123]" -syn match sshdconfigIPQoS "af4[123]" -syn match sshdconfigIPQoS "cs[0-7]" -syn keyword sshdconfigIPQoS ef lowdelay throughput reliability +syn match sshdconfigIPQoS "\<af[1-4][1-3]\>" +syn match sshdconfigIPQoS "\<cs[0-7]\>" +syn keyword sshdconfigIPQoS ef le lowdelay throughput reliability syn keyword sshdconfigKexAlgo diffie-hellman-group1-sha1 syn keyword sshdconfigKexAlgo diffie-hellman-group14-sha1 @@ -125,20 +121,20 @@ syn keyword sshdconfigKexAlgo diffie-hellman-group-exchange-sha256 syn keyword sshdconfigKexAlgo ecdh-sha2-nistp256 syn keyword sshdconfigKexAlgo ecdh-sha2-nistp384 syn keyword sshdconfigKexAlgo ecdh-sha2-nistp521 -syn keyword sshdconfigKexAlgo curve25519-sha256 -syn match sshdconfigKexAlgo "\<curve25519-sha256@libssh\.org\>" +syn match sshdconfigKexAlgo "\<curve25519-sha256\%(@libssh\.org\)\?\>" syn match sshdconfigKexAlgo "\<sntrup4591761x25519-sha512@tinyssh\.org\>" +syn match sshdconfigKexAlgo "\<sntrup761x25519-sha512@openssh\.com\>" syn keyword sshdconfigTunnel point-to-point ethernet syn keyword sshdconfigSubsystem internal-sftp -syn match sshdconfigVar "%[hu]\>" +syn match sshdconfigVar "%[CDFfhiKksTtUu]\>" syn match sshdconfigVar "%%" syn match sshdconfigSpecial "[*?]" -syn match sshdconfigNumber "\d\+" +syn match sshdconfigNumber "\<\d\+\>" syn match sshdconfigHostPort "\<\(\d\{1,3}\.\)\{3}\d\{1,3}\(:\d\+\)\?\>" syn match sshdconfigHostPort "\<\([-a-zA-Z0-9]\+\.\)\+[-a-zA-Z0-9]\{2,}\(:\d\+\)\?\>" " FIXME: this matches quite a few things which are NOT valid IPv6 addresses @@ -162,15 +158,16 @@ syn keyword sshdconfigKeyword AllowStreamLocalForwarding syn keyword sshdconfigKeyword AllowTcpForwarding syn keyword sshdconfigKeyword AllowUsers syn keyword sshdconfigKeyword AuthenticationMethods -syn keyword sshdconfigKeyword AuthorizedKeysFile syn keyword sshdconfigKeyword AuthorizedKeysCommand syn keyword sshdconfigKeyword AuthorizedKeysCommandUser +syn keyword sshdconfigKeyword AuthorizedKeysFile syn keyword sshdconfigKeyword AuthorizedPrincipalsCommand syn keyword sshdconfigKeyword AuthorizedPrincipalsCommandUser syn keyword sshdconfigKeyword AuthorizedPrincipalsFile syn keyword sshdconfigKeyword Banner syn keyword sshdconfigKeyword CASignatureAlgorithms syn keyword sshdconfigKeyword ChallengeResponseAuthentication +syn keyword sshdconfigKeyword ChannelTimeout syn keyword sshdconfigKeyword ChrootDirectory syn keyword sshdconfigKeyword Ciphers syn keyword sshdconfigKeyword ClientAliveCountMax @@ -187,22 +184,22 @@ syn keyword sshdconfigKeyword GatewayPorts syn keyword sshdconfigKeyword GSSAPIAuthentication syn keyword sshdconfigKeyword GSSAPICleanupCredentials syn keyword sshdconfigKeyword GSSAPIEnablek5users -syn keyword sshdconfigKeyword GSSAPIKeyExchange syn keyword sshdconfigKeyword GSSAPIKexAlgorithms +syn keyword sshdconfigKeyword GSSAPIKeyExchange syn keyword sshdconfigKeyword GSSAPIStoreCredentialsOnRekey syn keyword sshdconfigKeyword GSSAPIStrictAcceptorCheck -syn keyword sshdconfigKeyword HostCertificate -syn keyword sshdconfigKeyword HostKey -syn keyword sshdconfigKeyword HostKeyAgent -syn keyword sshdconfigKeyword HostKeyAlgorithms syn keyword sshdconfigKeyword HostbasedAcceptedAlgorithms syn keyword sshdconfigKeyword HostbasedAcceptedKeyTypes syn keyword sshdconfigKeyword HostbasedAuthentication syn keyword sshdconfigKeyword HostbasedUsesNameFromPacketOnly -syn keyword sshdconfigKeyword IPQoS +syn keyword sshdconfigKeyword HostCertificate +syn keyword sshdconfigKeyword HostKey +syn keyword sshdconfigKeyword HostKeyAgent +syn keyword sshdconfigKeyword HostKeyAlgorithms syn keyword sshdconfigKeyword IgnoreRhosts syn keyword sshdconfigKeyword IgnoreUserKnownHosts syn keyword sshdconfigKeyword Include +syn keyword sshdconfigKeyword IPQoS syn keyword sshdconfigKeyword KbdInteractiveAuthentication syn keyword sshdconfigKeyword KerberosAuthentication syn keyword sshdconfigKeyword KerberosGetAFSToken @@ -213,9 +210,9 @@ syn keyword sshdconfigKeyword KerberosUseKuserok syn keyword sshdconfigKeyword KexAlgorithms syn keyword sshdconfigKeyword KeyRegenerationInterval syn keyword sshdconfigKeyword ListenAddress +syn keyword sshdconfigKeyword LoginGraceTime syn keyword sshdconfigKeyword LogLevel syn keyword sshdconfigKeyword LogVerbose -syn keyword sshdconfigKeyword LoginGraceTime syn keyword sshdconfigKeyword MACs syn keyword sshdconfigKeyword Match syn keyword sshdconfigKeyword MaxAuthTries @@ -223,8 +220,6 @@ syn keyword sshdconfigKeyword MaxSessions syn keyword sshdconfigKeyword MaxStartups syn keyword sshdconfigKeyword ModuliFile syn keyword sshdconfigKeyword PasswordAuthentication -syn keyword sshdconfigKeyword PerSourceMaxStartups -syn keyword sshdconfigKeyword PerSourceNetBlockSize syn keyword sshdconfigKeyword PermitBlacklistedKeys syn keyword sshdconfigKeyword PermitEmptyPasswords syn keyword sshdconfigKeyword PermitListen @@ -234,6 +229,8 @@ syn keyword sshdconfigKeyword PermitTTY syn keyword sshdconfigKeyword PermitTunnel syn keyword sshdconfigKeyword PermitUserEnvironment syn keyword sshdconfigKeyword PermitUserRC +syn keyword sshdconfigKeyword PerSourceMaxStartups +syn keyword sshdconfigKeyword PerSourceNetBlockSize syn keyword sshdconfigKeyword PidFile syn keyword sshdconfigKeyword Port syn keyword sshdconfigKeyword PrintLastLog @@ -243,23 +240,24 @@ syn keyword sshdconfigKeyword PubkeyAcceptedAlgorithms syn keyword sshdconfigKeyword PubkeyAcceptedKeyTypes syn keyword sshdconfigKeyword PubkeyAuthentication syn keyword sshdconfigKeyword PubkeyAuthOptions -syn keyword sshdconfigKeyword RSAAuthentication +syn keyword sshdconfigKeyword RDomain syn keyword sshdconfigKeyword RekeyLimit syn keyword sshdconfigKeyword RequiredRSASize syn keyword sshdconfigKeyword RevokedKeys -syn keyword sshdconfigKeyword RDomain syn keyword sshdconfigKeyword RhostsRSAAuthentication +syn keyword sshdconfigKeyword RSAAuthentication syn keyword sshdconfigKeyword SecurityKeyProvider syn keyword sshdconfigKeyword ServerKeyBits syn keyword sshdconfigKeyword SetEnv syn keyword sshdconfigKeyword ShowPatchLevel -syn keyword sshdconfigKeyword StrictModes syn keyword sshdconfigKeyword StreamLocalBindMask syn keyword sshdconfigKeyword StreamLocalBindUnlink +syn keyword sshdconfigKeyword StrictModes syn keyword sshdconfigKeyword Subsystem syn keyword sshdconfigKeyword SyslogFacility syn keyword sshdconfigKeyword TCPKeepAlive syn keyword sshdconfigKeyword TrustedUserCAKeys +syn keyword sshdconfigKeyword UnusedConnectionTimeout syn keyword sshdconfigKeyword UseBlacklist syn keyword sshdconfigKeyword UseBlocklist syn keyword sshdconfigKeyword UseDNS @@ -278,14 +276,13 @@ syn keyword sshdconfigKeyword XAuthLocation hi def link sshdconfigComment Comment hi def link sshdconfigTodo Todo hi def link sshdconfigHostPort sshdconfigConstant -hi def link sshdconfigTime sshdconfigConstant -hi def link sshdconfigNumber sshdconfigConstant +hi def link sshdconfigTime Number +hi def link sshdconfigNumber Number hi def link sshdconfigConstant Constant -hi def link sshdconfigYesNo sshdconfigEnum +hi def link sshdconfigYesNo Boolean hi def link sshdconfigAddressFamily sshdconfigEnum hi def link sshdconfigPrivilegeSeparation sshdconfigEnum hi def link sshdconfigTcpForwarding sshdconfigEnum -hi def link sshdconfigRootLogin sshdconfigEnum hi def link sshdconfigCiphers sshdconfigEnum hi def link sshdconfigMAC sshdconfigEnum hi def link sshdconfigHostKeyAlgo sshdconfigEnum diff --git a/src/klib/kvec.h b/src/klib/kvec.h index a32b35a14c..1b9e6fd9f8 100644 --- a/src/klib/kvec.h +++ b/src/klib/kvec.h @@ -153,6 +153,12 @@ type init_array[INIT_SIZE]; \ } +#define KVI_INITIAL_VALUE(v) { \ + .size = 0, \ + .capacity = ARRAY_SIZE((v).init_array), \ + .items = (v).init_array \ +} + /// Initialize vector with preallocated array /// /// @param[out] v Vector to initialize. @@ -218,6 +224,17 @@ static inline void *_memcpy_free(void *const restrict dest, void *const restrict } \ } while (0) +#define kvi_concat_len(v, data, len) \ + if (len > 0) { \ + kvi_ensure_more_space(v, len); \ + assert((v).items); \ + memcpy((v).items + (v).size, data, sizeof((v).items[0]) * len); \ + (v).size = (v).size + len; \ + } + +#define kvi_concat(v, str) kvi_concat_len(v, str, strlen(str)) +#define kvi_splice(v1, v0) kvi_concat_len(v1, (v0).items, (v0).size) + /// Get location where to store new element to a vector with preallocated array /// /// @param[in,out] v Vector to push to. diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2b3ebb7bfb..2fb8f3d554 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -167,7 +167,7 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E /// @param[out] err Error details, if any /// // TODO(bfredl): val should take update vs reset flag -void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) +void nvim_set_hl(uint64_t channel_id, Integer ns_id, String name, Dict(highlight) *val, Error *err) FUNC_API_SINCE(7) { int hl_id = syn_check_group(name.data, name.size); @@ -184,7 +184,9 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); if (!ERROR_SET(err)) { - ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val); + WITH_SCRIPT_CONTEXT(channel_id, { + ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val); + }); } } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 00cfef23c7..c2cc8bd9b8 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1750,7 +1750,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force saveRedobuff(&save_redo); did_save_redobuff = true; } - did_filetype = keep_filetype; + curbuf->b_did_filetype = curbuf->b_keep_filetype; } // Note that we are applying autocmds. Some commands need to know. @@ -1760,7 +1760,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force // Remember that FileType was triggered. Used for did_filetype(). if (event == EVENT_FILETYPE) { - did_filetype = true; + curbuf->b_did_filetype = true; } char *tail = path_tail(fname); @@ -1864,7 +1864,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force if (did_save_redobuff) { restoreRedobuff(&save_redo); } - did_filetype = false; + curbuf->b_did_filetype = false; while (au_pending_free_buf != NULL) { buf_T *b = au_pending_free_buf->b_next; @@ -1901,7 +1901,7 @@ BYPASS_AU: } if (retval == OK && event == EVENT_FILETYPE) { - au_did_filetype = true; + curbuf->b_au_did_filetype = true; } return retval; @@ -2645,7 +2645,7 @@ void do_filetype_autocmd(buf_T *buf, bool force) secure = 0; ft_recursive++; - did_filetype = true; + buf->b_did_filetype = true; // Only pass true for "force" when it is true or // used recursively, to avoid endless recurrence. apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force || ft_recursive == 1, buf); diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 8019cb7145..b298fbb6a1 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -15,14 +15,6 @@ #include "nvim/pos_defs.h" #include "nvim/types_defs.h" -// Set by the apply_autocmds_group function if the given event is equal to -// EVENT_FILETYPE. Used by the readfile function in order to determine if -// EVENT_BUFREADPOST triggered the EVENT_FILETYPE. -// -// Relying on this value requires one to reset it prior calling -// apply_autocmds_group. -EXTERN bool au_did_filetype INIT( = false); - /// For CursorMoved event EXTERN win_T *last_cursormoved_win INIT( = NULL); /// For CursorMoved event, only used when last_cursormoved_win == curwin @@ -31,9 +23,6 @@ EXTERN pos_T last_cursormoved INIT( = { 0, 0, 0 }); EXTERN bool autocmd_busy INIT( = false); ///< Is apply_autocmds() busy? EXTERN int autocmd_no_enter INIT( = false); ///< Buf/WinEnter autocmds disabled EXTERN int autocmd_no_leave INIT( = false); ///< Buf/WinLeave autocmds disabled -EXTERN bool did_filetype INIT( = false); ///< FileType event found -/// value for did_filetype when starting to execute autocommands -EXTERN bool keep_filetype INIT( = false); /// When deleting the current buffer, another one must be loaded. /// If we know which one is preferred, au_new_curbuf is set to it. diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3c2d52e6ad..39d0d24d47 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -267,7 +267,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) // The autocommands in readfile() may change the buffer, but only AFTER // reading the file. set_bufref(&old_curbuf, curbuf); - modified_was_set = false; + curbuf->b_modified_was_set = false; // mark cursor position as being invalid curwin->w_valid = 0; @@ -350,7 +350,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) // the changed flag. Unless in readonly mode: "ls | nvim -R -". // When interrupted and 'cpoptions' contains 'i' set changed flag. if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - || modified_was_set // ":set modified" used in autocmd + || curbuf->b_modified_was_set // autocmd did ":set modified" || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { changed(curbuf); } else if (retval != FAIL && !read_stdin && !read_fifo) { @@ -1725,7 +1725,7 @@ void enter_buffer(buf_T *buf) // ":ball" used in an autocommand. If there already is a filetype we // might prefer to keep it. if (*curbuf->b_p_ft == NUL) { - did_filetype = false; + curbuf->b_did_filetype = false; } open_buffer(false, NULL, 0); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 7f7300706c..083508fe86 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -461,6 +461,19 @@ struct file_buffer { bool b_marks_read; // Have we read ShaDa marks yet? + bool b_modified_was_set; ///< did ":set modified" + bool b_did_filetype; ///< FileType event found + bool b_keep_filetype; ///< value for did_filetype when starting + ///< to execute autocommands + + /// Set by the apply_autocmds_group function if the given event is equal to + /// EVENT_FILETYPE. Used by the readfile function in order to determine if + /// EVENT_BUFREADPOST triggered the EVENT_FILETYPE. + /// + /// Relying on this value requires one to reset it prior calling + /// apply_autocmds_group(). + bool b_au_did_filetype; + // The following only used in undo.c. u_header_T *b_u_oldhead; // pointer to oldest header u_header_T *b_u_newhead; // pointer to newest header; may not be valid diff --git a/src/nvim/change.c b/src/nvim/change.c index b6f2be547f..fce0de49bb 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1838,6 +1838,13 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) saved_line = NULL; if (did_append) { + // Always move extmarks - Here we move only the line where the cursor is, + // the previous mark_adjust() took care of the lines after. + int cols_added = mincol - 1 + less_cols_off - less_cols; + extmark_splice(curbuf, (int)lnum - 1, mincol - 1 - cols_spliced, + 0, less_cols_off, less_cols_off, + 1, cols_added, 1 + cols_added, kExtmarkUndo); + changed_lines(curbuf, curwin->w_cursor.lnum, curwin->w_cursor.col, curwin->w_cursor.lnum + 1, 1, true); did_append = false; @@ -1848,12 +1855,6 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) curwin->w_cursor.col + less_cols_off, 1, -less_cols, 0); } - // Always move extmarks - Here we move only the line where the - // cursor is, the previous mark_adjust takes care of the lines after - int cols_added = mincol - 1 + less_cols_off - less_cols; - extmark_splice(curbuf, (int)lnum - 1, mincol - 1 - cols_spliced, - 0, less_cols_off, less_cols_off, - 1, cols_added, 1 + cols_added, kExtmarkUndo); } else { changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 8b8345657c..0326096486 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -365,9 +365,10 @@ static void insert_enter(InsertState *s) did_cursorhold = false; // ins_redraw() triggers TextChangedI only when no characters - // are in the typeahead buffer, so only reset curbuf->b_last_changedtick + // are in the typeahead buffer, so reset curbuf->b_last_changedtick // if the TextChangedI was not blocked by char_avail() (e.g. using :norm!) - if (!char_avail()) { + // and the TextChangedI autocommand has been triggered + if (!char_avail() && curbuf->b_last_changedtick_i == buf_get_changedtick(curbuf)) { curbuf->b_last_changedtick = buf_get_changedtick(curbuf); } } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 99da15ddd7..d3fe2c7e33 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1361,7 +1361,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp /// "did_filetype()" function static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - rettv->vval.v_number = did_filetype; + rettv->vval.v_number = curbuf->b_did_filetype; } /// "diff_filler()" function diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index dd30cdbba4..6c997b9500 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2466,7 +2466,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum // Since we are starting to edit a file, consider the filetype to be // unset. Helps for when an autocommand changes files and expects syntax // highlighting to work in the other file. - did_filetype = false; + curbuf->b_did_filetype = false; // other_file oldbuf // false false re-edit same file, buffer is re-used diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ee738d9e92..318d2e4ff2 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7382,7 +7382,7 @@ void filetype_maybe_enable(void) /// ":setfiletype [FALLBACK] {name}" static void ex_setfiletype(exarg_T *eap) { - if (did_filetype) { + if (curbuf->b_did_filetype) { return; } @@ -7393,7 +7393,7 @@ static void ex_setfiletype(exarg_T *eap) set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL); if (arg != eap->arg) { - did_filetype = false; + curbuf->b_did_filetype = false; } } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 6b50b031a3..df9c4928c9 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -234,7 +234,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, int using_b_fname; static char *msg_is_a_directory = N_("is a directory"); - au_did_filetype = false; // reset before triggering any autocommands + curbuf->b_au_did_filetype = false; // reset before triggering any autocommands curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read @@ -1854,7 +1854,7 @@ failed: } else if (newfile || (read_buffer && sfname != NULL)) { apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname, false, curbuf, eap); - if (!au_did_filetype && *curbuf->b_p_ft != NUL) { + if (!curbuf->b_au_did_filetype && *curbuf->b_p_ft != NUL) { // EVENT_FILETYPE was not triggered but the buffer already has a // filetype. Trigger EVENT_FILETYPE using the existing filetype. apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, true, curbuf); @@ -3151,7 +3151,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) if (saved == OK) { curbuf->b_flags |= BF_CHECK_RO; // check for RO again - keep_filetype = true; // don't detect 'filetype' + curbuf->b_keep_filetype = true; // don't detect 'filetype' if (readfile(buf->b_ffname, buf->b_fname, 0, 0, (linenr_T)MAXLNUM, &ea, flags, shortmess(SHM_FILEINFO)) != OK) { if (!aborting()) { @@ -3199,7 +3199,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) curwin->w_cursor = old_cursor; check_cursor(curwin); update_topline(curwin); - keep_filetype = false; + curbuf->b_keep_filetype = false; // Update folds unless they are defined manually. FOR_ALL_TAB_WINDOWS(tp, wp) { diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index f68bd7098b..665f60a49e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -94,6 +94,12 @@ static buffheader_T readbuf1 = { { NULL, { NUL } }, NULL, 0, 0 }; /// Second read ahead buffer. Used for redo. static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 }; +/// Buffer used to store typed characters for vim.on_key(). +static kvec_withinit_t(char, MAXMAPLEN) on_key_buf = KVI_INITIAL_VALUE(on_key_buf); + +/// Number of following bytes that should not be stored for vim.on_key(). +static size_t no_on_key_len = 0; + static int typeahead_char = 0; ///< typeahead char that's not flushed /// When block_redo is true the redo buffer will not be changed. @@ -994,14 +1000,19 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent) /// Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to /// the char. /// +/// @param no_on_key don't store these bytes for vim.on_key() +/// /// @return the length of what was inserted -int ins_char_typebuf(int c, int modifiers) +int ins_char_typebuf(int c, int modifiers, bool no_on_key) { char buf[MB_MAXBYTES * 3 + 4]; unsigned len = special_to_buf(c, modifiers, true, buf); assert(len < sizeof(buf)); buf[len] = NUL; ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + if (KeyTyped && no_on_key) { + no_on_key_len += len; + } return (int)len; } @@ -1102,38 +1113,82 @@ void del_typebuf(int len, int offset) } /// Write typed characters to script file. -/// If recording is on put the character in the recordbuffer. +/// If recording is on put the character in the record buffer. static void gotchars(const uint8_t *chars, size_t len) FUNC_ATTR_NONNULL_ALL { const uint8_t *s = chars; - static uint8_t buf[4] = { 0 }; + int c = NUL; + static int prev_c = NUL; + static uint8_t buf[MB_MAXBYTES * 3 + 4] = { 0 }; static size_t buflen = 0; + static unsigned pending = 0; + static bool in_special = false; + static bool in_mbyte = false; size_t todo = len; - while (todo--) { - buf[buflen++] = *s++; + for (; todo--; prev_c = c) { + c = buf[buflen++] = *s++; + if (pending > 0) { + pending--; + } // When receiving a special key sequence, store it until we have all // the bytes and we can decide what to do with it. - if (buflen == 1 && buf[0] == K_SPECIAL) { - continue; + if ((pending == 0 || in_mbyte) && c == K_SPECIAL) { + pending += 2; + if (!in_mbyte) { + in_special = true; + } } - if (buflen == 2) { + + if (pending > 0) { continue; } + if (!in_mbyte) { + if (in_special) { + in_special = false; + if (prev_c == KS_MODIFIER) { + // When receiving a modifier, wait for the modified key. + continue; + } + c = TO_SPECIAL(prev_c, c); + } + // When receiving a multibyte character, store it until we have all + // the bytes, so that it won't be split between two buffer blocks, + // and delete_buff_tail() will work properly. + pending = MB_BYTE2LEN_CHECK(c) - 1; + if (pending > 0) { + in_mbyte = true; + continue; + } + } else { + // Stored all bytes of a multibyte character. + in_mbyte = false; + } + // Handle one byte at a time; no translation to be done. for (size_t i = 0; i < buflen; i++) { updatescript(buf[i]); } + buf[buflen] = NUL; + if (reg_recording != 0) { - buf[buflen] = NUL; add_buff(&recordbuff, (char *)buf, (ptrdiff_t)buflen); // remember how many chars were last recorded last_recorded_len += buflen; } + + if (buflen > no_on_key_len) { + vim_unescape_ks((char *)buf + no_on_key_len); + kvi_concat(on_key_buf, (char *)buf + no_on_key_len); + no_on_key_len = 0; + } else { + no_on_key_len -= buflen; + } + buflen = 0; } @@ -1151,6 +1206,7 @@ static void gotchars(const uint8_t *chars, size_t len) void gotchars_ignore(void) { uint8_t nop_buf[3] = { K_SPECIAL, KS_EXTRA, KE_IGNORE }; + no_on_key_len += 3; gotchars(nop_buf, 3); } @@ -1621,9 +1677,13 @@ int vgetc(void) if (!no_mapping && KeyTyped && mod_mask == MOD_MASK_ALT && !(State & MODE_TERMINAL) && !is_mouse_key(c)) { mod_mask = 0; - int len = ins_char_typebuf(c, 0); - ins_char_typebuf(ESC, 0); - ungetchars(len + 3); // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes + int len = ins_char_typebuf(c, 0, false); + ins_char_typebuf(ESC, 0, false); + int old_len = len + 3; // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes + ungetchars(old_len); + if (on_key_buf.size >= (size_t)old_len) { + on_key_buf.size -= (size_t)old_len; + } continue; } @@ -1639,7 +1699,9 @@ int vgetc(void) may_garbage_collect = false; // Execute Lua on_key callbacks. - nlua_execute_on_key(c); + nlua_execute_on_key(c, on_key_buf.items, on_key_buf.size); + kvi_destroy(on_key_buf); + kvi_init(on_key_buf); // Need to process the character before we know it's safe to do something // else. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index bb9aca38b7..83fef1fe75 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -341,8 +341,6 @@ EXTERN bool did_check_timestamps INIT( = false); // did check timestamps // recently EXTERN int no_check_timestamps INIT( = 0); // Don't check timestamps -EXTERN int modified_was_set; // did ":set modified" - // Mouse coordinates, set by handle_mouse_event() EXTERN int mouse_grid; EXTERN int mouse_row; diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index f4800deb90..05b6dfb896 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -889,11 +889,13 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) g->sg_link = link_id; g->sg_script_ctx = current_sctx; g->sg_script_ctx.sc_lnum += SOURCING_LNUM; + nlua_set_sctx(&g->sg_script_ctx); g->sg_set |= SG_LINK; if (is_default) { g->sg_deflink = link_id; g->sg_deflink_sctx = current_sctx; g->sg_deflink_sctx.sc_lnum += SOURCING_LNUM; + nlua_set_sctx(&g->sg_deflink_sctx); } } else { g->sg_link = 0; @@ -934,6 +936,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) g->sg_script_ctx = current_sctx; g->sg_script_ctx.sc_lnum += SOURCING_LNUM; + nlua_set_sctx(&g->sg_script_ctx); g->sg_attr = hl_get_syn_attr(0, id, attrs); @@ -1914,7 +1917,7 @@ static void set_hl_attr(int idx) at_en.cterm_bg_color = (int16_t)sgp->sg_cterm_bg; at_en.rgb_ae_attr = (int16_t)sgp->sg_gui; // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is - // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name + // initialized with 0 (by garray functions), check for sg_rgb_{f,b}g_name // before setting attr_entry->{f,g}g_color to a other than -1 at_en.rgb_fg_color = sgp->sg_rgb_fg_idx != kColorIdxNone ? sgp->sg_rgb_fg : -1; at_en.rgb_bg_color = sgp->sg_rgb_bg_idx != kColorIdxNone ? sgp->sg_rgb_bg : -1; @@ -2472,7 +2475,7 @@ color_name_table_T color_name_table[] = { { "Cyan4", RGB_(0x0, 0x8b, 0x8b) }, { "DarkBlue", RGB_(0x00, 0x00, 0x8b) }, { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) }, - { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) }, + { "DarkGoldenrod", RGB_(0xb8, 0x86, 0x0b) }, { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) }, { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) }, { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) }, @@ -2546,7 +2549,7 @@ color_name_table_T color_name_table[] = { { "Gold2", RGB_(0xee, 0xc9, 0x0) }, { "Gold3", RGB_(0xcd, 0xad, 0x0) }, { "Gold4", RGB_(0x8b, 0x75, 0x0) }, - { "GoldenRod", RGB_(0xda, 0xa5, 0x20) }, + { "Goldenrod", RGB_(0xda, 0xa5, 0x20) }, { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) }, { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) }, { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) }, @@ -2815,7 +2818,7 @@ color_name_table_T color_name_table[] = { { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) }, { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) }, { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) }, - { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) }, + { "LightGoldenrodYellow", RGB_(0xfa, 0xfa, 0xd2) }, { "LightGray", RGB_(0xd3, 0xd3, 0xd3) }, { "LightGreen", RGB_(0x90, 0xee, 0x90) }, { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) }, @@ -2900,6 +2903,10 @@ color_name_table_T color_name_table[] = { // for foreground in light/dark color scheme. { "NvimDarkBlue", RGB_(0x00, 0x4c, 0x73) }, { "NvimDarkCyan", RGB_(0x00, 0x73, 0x73) }, + { "NvimDarkGray1", RGB_(0x07, 0x08, 0x0d) }, + { "NvimDarkGray2", RGB_(0x14, 0x16, 0x1b) }, + { "NvimDarkGray3", RGB_(0x2c, 0x2e, 0x33) }, + { "NvimDarkGray4", RGB_(0x4f, 0x52, 0x58) }, { "NvimDarkGreen", RGB_(0x00, 0x55, 0x23) }, { "NvimDarkGrey1", RGB_(0x07, 0x08, 0x0d) }, { "NvimDarkGrey2", RGB_(0x14, 0x16, 0x1b) }, @@ -2910,6 +2917,10 @@ color_name_table_T color_name_table[] = { { "NvimDarkYellow", RGB_(0x6b, 0x53, 0x00) }, { "NvimLightBlue", RGB_(0xa6, 0xdb, 0xff) }, { "NvimLightCyan", RGB_(0x8c, 0xf8, 0xf7) }, + { "NvimLightGray1", RGB_(0xee, 0xf1, 0xf8) }, + { "NvimLightGray2", RGB_(0xe0, 0xe2, 0xea) }, + { "NvimLightGray3", RGB_(0xc4, 0xc6, 0xcd) }, + { "NvimLightGray4", RGB_(0x9b, 0x9e, 0xa4) }, { "NvimLightGreen", RGB_(0xb3, 0xf6, 0xc0) }, { "NvimLightGrey1", RGB_(0xee, 0xf1, 0xf8) }, { "NvimLightGrey2", RGB_(0xe0, 0xe2, 0xea) }, @@ -2940,7 +2951,7 @@ color_name_table_T color_name_table[] = { { "Orchid2", RGB_(0xee, 0x7a, 0xe9) }, { "Orchid3", RGB_(0xcd, 0x69, 0xc9) }, { "Orchid4", RGB_(0x8b, 0x47, 0x89) }, - { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) }, + { "PaleGoldenrod", RGB_(0xee, 0xe8, 0xaa) }, { "PaleGreen", RGB_(0x98, 0xfb, 0x98) }, { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) }, { "PaleGreen2", RGB_(0x90, 0xee, 0x90) }, diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h index 47d58d20f2..edf5fbde16 100644 --- a/src/nvim/highlight_group.h +++ b/src/nvim/highlight_group.h @@ -12,7 +12,7 @@ typedef struct { char *name; RgbValue color; } color_name_table_T; -extern color_name_table_T color_name_table[700]; +extern color_name_table_T color_name_table[708]; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight_group.h.generated.h" diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index d5d35c5295..cfc68dc08f 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -2064,9 +2064,9 @@ char *nlua_register_table_as_callable(const typval_T *const arg) return name; } -void nlua_execute_on_key(int c) +void nlua_execute_on_key(int c, char *typed_buf, size_t typed_len) { - char buf[NUMBUFLEN]; + char buf[MB_MAXBYTES * 3 + 4]; size_t buf_len = special_to_buf(c, mod_mask, false, buf); lua_State *const lstate = global_lstate; @@ -2085,9 +2085,12 @@ void nlua_execute_on_key(int c) // [ vim, vim._on_key, buf ] lua_pushlstring(lstate, buf, buf_len); + // [ vim, vim._on_key, buf, typed_buf ] + lua_pushlstring(lstate, typed_buf, typed_len); + int save_got_int = got_int; got_int = false; // avoid interrupts when the key typed is Ctrl-C - if (nlua_pcall(lstate, 1, 0)) { + if (nlua_pcall(lstate, 2, 0)) { nlua_error(lstate, _("Error executing vim.on_key Lua callback: %.*s")); } diff --git a/src/nvim/message.c b/src/nvim/message.c index 5a47908eb6..68a8b8e88b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1265,7 +1265,7 @@ void wait_return(int redraw) } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) { // Put the character back in the typeahead buffer. Don't use the // stuff buffer, because lmaps wouldn't work. - ins_char_typebuf(vgetc_char, vgetc_mod_mask); + ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); do_redraw = true; // need a redraw even though there is // typeahead } @@ -3431,7 +3431,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt } if (c == ':' && ex_cmd) { retval = dfltbutton; - ins_char_typebuf(':', 0); + ins_char_typebuf(':', 0, false); break; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index bbba8069c7..c7eb5c5793 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1078,7 +1078,7 @@ static int normal_execute(VimState *state, int key) // When "restart_edit" is set fake a "d"elete command, Insert mode will restart automatically. // Insert the typed character in the typeahead buffer, so that it can // be mapped in Insert mode. Required for ":lmap" to work. - int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask); + int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); // When recording and gotchars() was called the character will be // recorded again, remove the previous recording. diff --git a/src/nvim/option.c b/src/nvim/option.c index 2c7922908c..799513e018 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2244,7 +2244,7 @@ static const char *did_set_modified(optset_T *args) save_file_ff(buf); // Buffer is unchanged } redraw_titles(); - modified_was_set = (int)args->os_newval.boolean; + buf->b_modified_was_set = (int)args->os_newval.boolean; return NULL; } diff --git a/src/nvim/options.lua b/src/nvim/options.lua index e7acf6f6ef..1d81b570b7 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7663,8 +7663,7 @@ return { highlighted with |hl-NonText|. You may also want to add "lastline" to the 'display' option to show as much of the last line as possible. - NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y, - CTRL-D, CTRL-U, CTRL-F, CTRL-B and scrolling with the mouse. + NOTE: partly implemented, doesn't work yet for |gj| and |gk|. ]=], full_name = 'smoothscroll', pv_name = 'p_sms', diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index baa99fca65..5218e50df6 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -255,9 +255,9 @@ size_t input_enqueue(String keys) while (rbuffer_space(input_buffer) >= 19 && ptr < end) { // A "<x>" form occupies at least 1 characters, and produces up // to 19 characters (1 + 5 * 3 for the char and 3 for a modifier). - // In the case of K_SPECIAL(0x80), 3 bytes are escaped and needed, + // In the case of K_SPECIAL (0x80), 3 bytes are escaped and needed, // but since the keys are UTF-8, so the first byte cannot be - // K_SPECIAL(0x80). + // K_SPECIAL (0x80). uint8_t buf[19] = { 0 }; // Do not simplify the keys here. Simplification will be done later. unsigned new_size diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 9f431556e8..94e176bd94 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4274,10 +4274,10 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL); curbuf->b_p_ma = false; - keep_filetype = true; // don't detect 'filetype' + curbuf->b_keep_filetype = true; // don't detect 'filetype' apply_autocmds(EVENT_BUFREADPOST, "quickfix", NULL, false, curbuf); apply_autocmds(EVENT_BUFWINENTER, "quickfix", NULL, false, curbuf); - keep_filetype = false; + curbuf->b_keep_filetype = false; curbuf->b_ro_locked--; // make sure it will be redrawn diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 76188499e3..bc9ace0fa8 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -2392,7 +2392,7 @@ char *get_scriptname(LastSet last_set, bool *should_free) case SID_WINLAYOUT: return _("changed window size"); case SID_LUA: - return _("Lua"); + return _("Lua (run Nvim with -V1 for more details)"); case SID_API_CLIENT: snprintf(IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id); return IObuff; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 3734d55a5b..2b05a8047e 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1631,7 +1631,10 @@ end: return false; } - ins_char_typebuf(vgetc_char, vgetc_mod_mask); + int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); + if (KeyTyped) { + ungetchars(len); + } return true; } diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index d98308ee87..dc36926641 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1593,14 +1593,12 @@ describe('API', function() api.nvim_set_option_value('equalalways', false, {}) local status, rv = pcall(command_output, 'verbose set equalalways?') eq(true, status) - ok( - nil ~= string.find(rv, 'noequalalways\n' .. '\tLast set from API client %(channel id %d+%)') - ) + matches('noequalalways\n' .. '\tLast set from API client %(channel id %d+%)', rv) api.nvim_exec_lua('vim.api.nvim_set_option_value("equalalways", true, {})', {}) status, rv = pcall(command_output, 'verbose set equalalways?') eq(true, status) - eq(' equalalways\n\tLast set from Lua', rv) + eq(' equalalways\n\tLast set from Lua (run Nvim with -V1 for more details)', rv) end) it('updates whether the option has ever been set #25025', function() @@ -2723,7 +2721,7 @@ describe('API', function() it('can throw exceptions', function() local status, err = pcall(api.nvim_get_option_value, 'invalid-option', {}) eq(false, status) - ok(err:match("Unknown option 'invalid%-option'") ~= nil) + matches("Unknown option 'invalid%-option'", err) end) it('does not truncate error message <1 MB #5984', function() @@ -2736,10 +2734,7 @@ describe('API', function() it('does not leak memory on incorrect argument types', function() local status, err = pcall(api.nvim_set_current_dir, { 'not', 'a', 'dir' }) eq(false, status) - ok( - err:match(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String') - ~= nil - ) + matches(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String', err) end) describe('nvim_parse_expression', function() @@ -3670,7 +3665,7 @@ describe('API', function() api.nvim_buf_set_name(buf, 'mybuf') local mark = api.nvim_get_mark('F', {}) -- Compare the path tail only - assert(string.find(mark[4], 'mybuf$')) + matches('mybuf$', mark[4]) eq({ 2, 2, buf, mark[4] }, mark) end) it('validation', function() diff --git a/test/functional/autocmd/textchanged_spec.lua b/test/functional/autocmd/textchanged_spec.lua index d501560dc1..8d8058da70 100644 --- a/test/functional/autocmd/textchanged_spec.lua +++ b/test/functional/autocmd/textchanged_spec.lua @@ -155,10 +155,9 @@ it('TextChangedI and TextChanged', function() feed('yypi<esc>') eq('', eval('g:autocmd_i')) - -- TextChanged should only trigger if change was done in Normal mode command([[let g:autocmd_n = '']]) feed('ibar<esc>') - eq('', eval('g:autocmd_n')) + eq('N8', eval('g:autocmd_n')) local function validate_mixed_textchangedi(keys) feed('ifoo<esc>') @@ -191,3 +190,26 @@ it('TextChanged is triggered after :norm that enters Insert mode', function() feed(':norm! ia<CR>') eq(1, eval('g:a')) end) + +-- oldtest: Test_Changed_ChangedI_2() +it('TextChanged is triggered after mapping that enters & exits Insert mode', function() + exec([[ + let [g:autocmd_i, g:autocmd_n] = ['',''] + + func! TextChangedAutocmdI(char) + let g:autocmd_{tolower(a:char)} = a:char .. b:changedtick + endfunc + + augroup Test_TextChanged + au! + au TextChanged <buffer> :call TextChangedAutocmdI('N') + au TextChangedI <buffer> :call TextChangedAutocmdI('I') + augroup END + + nnoremap <CR> o<Esc> + ]]) + + feed('<CR>') + eq('N3', eval('g:autocmd_n')) + eq('', eval('g:autocmd_i')) +end) diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 56a2f5a571..0ab885e02f 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, eval, next_msg, ok, source = helpers.clear, helpers.eq, helpers.eval, helpers.next_msg, helpers.ok, helpers.source local command, fn, api = helpers.command, helpers.fn, helpers.api +local matches = helpers.matches local sleep = vim.uv.sleep local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv local get_session, set_session = helpers.get_session, helpers.set_session @@ -277,7 +278,7 @@ describe('channels', function() local _, err = pcall(command, "call rpcrequest(id, 'nvim_command', 'call chanclose(v:stderr, \"stdin\")')") - ok(string.find(err, 'E906: invalid stream for channel') ~= nil) + matches('E906: invalid stream for channel', err) eq(1, eval("rpcrequest(id, 'nvim_eval', 'chanclose(v:stderr, \"stderr\")')")) eq({ 'notification', 'stderr', { 3, { '' } } }, next_msg()) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 9e7f1faa47..f0e61fbcc1 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -73,7 +73,7 @@ describe('jobs', function() command("let j = jobstart('env', g:job_opts)") end end) - ok(string.find(err, 'E475: Invalid argument: env') ~= nil) + matches('E475: Invalid argument: env', err) end) it('append environment #env', function() @@ -227,7 +227,7 @@ describe('jobs', function() command("let j = jobstart('pwd', g:job_opts)") end end) - ok(string.find(err, 'E475: Invalid argument: expected valid directory$') ~= nil) + matches('E475: Invalid argument: expected valid directory$', err) end) it('error on non-executable `cwd`', function() @@ -980,10 +980,7 @@ describe('jobs', function() command('let g:job_opts.pty = v:true') command('let g:job_opts.rpc = v:true') local _, err = pcall(command, "let j = jobstart(['cat', '-'], g:job_opts)") - ok( - string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") - ~= nil - ) + matches("E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set", err) end) it('does not crash when repeatedly failing to start shell', function() @@ -1239,6 +1236,7 @@ describe('pty process teardown', function() end) it('does not prevent/delay exit. #4798 #4900', function() + skip(fn.executable('sleep') == 0, 'missing "sleep" command') -- Use a nested nvim (in :term) to test without --headless. fn.termopen({ helpers.nvim_prog, diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua index b57f5c3c35..e2bdf6ba96 100644 --- a/test/functional/editor/meta_key_spec.lua +++ b/test/functional/editor/meta_key_spec.lua @@ -141,4 +141,29 @@ describe('meta-keys #8226 #13042', function() // This is some text: bar // This is some text: baz]]) end) + + it('ALT/META with vim.on_key()', function() + feed('ifoo<CR>bar<CR>baz<Esc>gg0') + + exec_lua [[ + keys = {} + typed = {} + + vim.on_key(function(buf, typed_buf) + table.insert(keys, vim.fn.keytrans(buf)) + table.insert(typed, vim.fn.keytrans(typed_buf)) + end) + ]] + + -- <M-"> is reinterpreted as <Esc>" + feed('qrviw"ayc$FOO.<M-">apq') + expect([[ + FOO.foo + bar + baz]]) + + -- vim.on_key() callback should only receive <Esc>" + eq('qrviw"ayc$FOO.<Esc>"apq', exec_lua [[return table.concat(keys, '')]]) + eq('qrviw"ayc$FOO.<Esc>"apq', exec_lua [[return table.concat(typed, '')]]) + end) end) diff --git a/test/functional/ex_cmds/arg_spec.lua b/test/functional/ex_cmds/arg_spec.lua index 810b001ec0..45b2f50af1 100644 --- a/test/functional/ex_cmds/arg_spec.lua +++ b/test/functional/ex_cmds/arg_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local eq, command, fn = helpers.eq, helpers.command, helpers.fn local ok = helpers.ok +local matches = helpers.matches local clear = helpers.clear describe(':argument', function() @@ -15,7 +16,7 @@ describe(':argument', function() helpers.feed([[<C-\><C-N>]]) local bufname_before = fn.bufname('%') local bufnr_before = fn.bufnr('%') - helpers.ok(nil ~= string.find(bufname_before, '^term://')) -- sanity + matches('^term://', bufname_before) -- sanity command('argument 1') helpers.feed([[<C-\><C-N>]]) diff --git a/test/functional/ex_cmds/edit_spec.lua b/test/functional/ex_cmds/edit_spec.lua index b927fa418a..9d4effd72e 100644 --- a/test/functional/ex_cmds/edit_spec.lua +++ b/test/functional/ex_cmds/edit_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local eq, command, fn = helpers.eq, helpers.command, helpers.fn local ok = helpers.ok +local matches = helpers.matches local clear = helpers.clear local feed = helpers.feed @@ -14,7 +15,7 @@ describe(':edit', function() feed([[<C-\><C-N>]]) local bufname_before = fn.bufname('%') local bufnr_before = fn.bufnr('%') - helpers.ok(nil ~= string.find(bufname_before, '^term://')) -- sanity + matches('^term://', bufname_before) -- sanity command('edit') diff --git a/test/functional/ex_cmds/verbose_spec.lua b/test/functional/ex_cmds/verbose_spec.lua index 7ceb2460d3..ba90793a37 100644 --- a/test/functional/ex_cmds/verbose_spec.lua +++ b/test/functional/ex_cmds/verbose_spec.lua @@ -32,21 +32,30 @@ vim.api.nvim_exec2("augroup test_group\ augroup END\ ", {}) +vim.api.nvim_create_autocmd('FileType', { + group = 'test_group', + pattern = 'cpp', + command = 'setl cindent', +}) + +vim.api.nvim_exec2(':highlight TestHL1 guibg=Blue', {}) +vim.api.nvim_set_hl(0, 'TestHL2', { bg = 'Green' }) + vim.api.nvim_command("command Bdelete :bd") vim.api.nvim_create_user_command("TestCommand", ":echo 'Hello'", {}) -vim.api.nvim_exec ("\ +vim.api.nvim_exec2 ("\ function Close_Window() abort\ wincmd -\ endfunction\ -", false) +", {}) -local ret = vim.api.nvim_exec ("\ +local ret = vim.api.nvim_exec2 ("\ function! s:return80()\ return 80\ endfunction\ let &tw = s:return80()\ -", true) +", {}) ]] ) exec(cmd .. ' ' .. script_file) @@ -109,7 +118,7 @@ n \key1 * :echo "test"<CR> ) end) - it('"Last set" for mapping set by vim.keymap', function() + it('"Last set" for mapping set by vim.keymap.set', function() local result = exec_capture(':verbose map <leader>key2') eq( string.format( @@ -123,7 +132,7 @@ n \key2 * :echo "test"<CR> ) end) - it('"Last set" for autocmd by vim.api.nvim_exec', function() + it('"Last set" for autocmd set by nvim_exec2', function() local result = exec_capture(':verbose autocmd test_group Filetype c') eq( string.format( @@ -138,6 +147,47 @@ test_group FileType ) end) + it('"Last set" for autocmd set by nvim_create_autocmd', function() + local result = exec_capture(':verbose autocmd test_group Filetype cpp') + eq( + string.format( + [[ +--- Autocommands --- +test_group FileType + cpp setl cindent + Last set from %s line 13]], + script_location + ), + result + ) + end) + + it('"Last set" for highlight group set by nvim_exec2', function() + local result = exec_capture(':verbose highlight TestHL1') + eq( + string.format( + [[ +TestHL1 xxx guibg=Blue + Last set from %s line 19]], + script_location + ), + result + ) + end) + + it('"Last set" for highlight group set by nvim_set_hl', function() + local result = exec_capture(':verbose highlight TestHL2') + eq( + string.format( + [[ +TestHL2 xxx guibg=Green + Last set from %s line 20]], + script_location + ), + result + ) + end) + it('"Last set" for command defined by nvim_command', function() if cmd == 'luafile' then pending('nvim_command does not set the script context') @@ -148,7 +198,7 @@ test_group FileType [[ Name Args Address Complete Definition Bdelete 0 :bd - Last set from %s line 13]], + Last set from %s line 22]], script_location ), result @@ -162,7 +212,7 @@ test_group FileType [[ Name Args Address Complete Definition TestCommand 0 :echo 'Hello' - Last set from %s line 14]], + Last set from %s line 23]], script_location ), result @@ -175,7 +225,7 @@ test_group FileType string.format( [[ function Close_Window() abort - Last set from %s line 16 + Last set from %s line 25 1 wincmd - endfunction]], script_location @@ -190,7 +240,7 @@ test_group FileType string.format( [[ textwidth=80 - Last set from %s line 22]], + Last set from %s line 31]], script_location ), result @@ -230,7 +280,7 @@ describe('lua verbose:', function() eq( [[ nohlsearch - Last set from Lua]], + Last set from Lua (run Nvim with -V1 for more details)]], result ) end) diff --git a/test/functional/legacy/normal_spec.lua b/test/functional/legacy/normal_spec.lua index 8b7acccc2c..3dac1da14a 100644 --- a/test/functional/legacy/normal_spec.lua +++ b/test/functional/legacy/normal_spec.lua @@ -2,22 +2,28 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear = helpers.clear local exec = helpers.exec - -before_each(clear) +local feed = helpers.feed +local api = helpers.api +local eq = helpers.eq +local fn = helpers.fn describe('normal', function() + local screen + + before_each(function() + clear() + screen = Screen.new(40, 19) + screen:attach() + end) + -- oldtest: Test_normal_j_below_botline() - it( - [["j" does not skip lines when scrolling below botline and 'foldmethod' is not "manual"]], - function() - local screen = Screen.new(40, 19) - screen:attach() - exec([[ + it([[no skipped lines with "j" scrolling below botline and 'foldmethod' not "manual"]], function() + exec([[ set number foldmethod=diff scrolloff=0 call setline(1, map(range(1, 9), 'repeat(v:val, 200)')) norm Lj ]]) - screen:expect([[ + screen:expect([[ {8: 2 }222222222222222222222222222222222222| {8: }222222222222222222222222222222222222|*4 {8: }22222222222222222222 | @@ -29,6 +35,71 @@ describe('normal', function() {8: }44444444444444444444 | | ]]) - end - ) + end) + + -- oldtest: Test_single_line_scroll() + it('(Half)-page scroll up or down reveals virtual lines #19605, #27967', function() + fn.setline(1, 'foobar one two three') + exec('set smoothscroll') + local ns = api.nvim_create_namespace('') + api.nvim_buf_set_extmark(0, ns, 0, 0, { + virt_lines = { { { '---', 'IncSearch' } } }, + virt_lines_above = true, + }) + -- Nvim: not actually necessary to scroll down to hide the virtual line. + -- Check topfill instead of skipcol and show the screen state. + feed('<C-E>') + eq(0, fn.winsaveview().topfill) + local s1 = [[ + ^foobar one two three | + {1:~ }|*17 + | + ]] + screen:expect(s1) + feed('<C-B>') + eq(1, fn.winsaveview().topfill) + local s2 = [[ + {2:---} | + ^foobar one two three | + {1:~ }|*16 + | + ]] + screen:expect(s2) + feed('<C-E>') + eq(0, fn.winsaveview().topfill) + screen:expect(s1) + feed('<C-U>') + eq(1, fn.winsaveview().topfill) + screen:expect(s2) + + -- Nvim: also test virt_lines below the last line + feed('yy100pG<C-L>') + api.nvim_buf_set_extmark(0, ns, 100, 0, { virt_lines = { { { '---', 'IncSearch' } } } }) + screen:expect({ + grid = [[ + foobar one two three |*17 + ^foobar one two three | + | + ]], + }) + feed('<C-F>') + screen:expect({ + grid = [[ + ^foobar one two three | + {2:---} | + {1:~ }|*16 + | + ]], + }) + feed('ggG<C-D>') + screen:expect({ + grid = [[ + foobar one two three |*8 + ^foobar one two three | + {2:---} | + {1:~ }|*8 + | + ]], + }) + end) end) diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 2479a433e6..01dc8c2f02 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -1,5 +1,6 @@ -- Test suite for testing interactions with API bindings local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local command = helpers.command local api = helpers.api @@ -323,20 +324,60 @@ describe('lua buffer event callbacks: on_lines', function() eq({ 'lines', 1, 6, 0, 3, 3, 9 }, api.nvim_get_var('linesev')) end) - it( - 'calling nvim_buf_call() from callback does not cause Normal mode CTRL-A to misbehave #16729', - function() - exec_lua([[ + it('nvim_buf_call() from callback does not cause wrong Normal mode CTRL-A #16729', function() + exec_lua([[ vim.api.nvim_buf_attach(0, false, { on_lines = function(...) vim.api.nvim_buf_call(0, function() end) end, }) ]]) - feed('itest123<Esc><C-A>') - eq('test124', api.nvim_get_current_line()) - end - ) + feed('itest123<Esc><C-A>') + eq('test124', api.nvim_get_current_line()) + end) + + it('setting extmark in on_lines callback works', function() + local screen = Screen.new(40, 6) + screen:attach() + + api.nvim_buf_set_lines(0, 0, -1, true, { 'aaa', 'bbb', 'ccc' }) + exec_lua([[ + local ns = vim.api.nvim_create_namespace('') + vim.api.nvim_buf_attach(0, false, { + on_lines = function(_, _, _, row, _, end_row) + vim.api.nvim_buf_clear_namespace(0, ns, row, end_row) + for i = row, end_row - 1 do + local id = vim.api.nvim_buf_set_extmark(0, ns, i, 0, { + virt_text = {{ 'NEW' .. tostring(i), 'WarningMsg' }}, + }) + end + end, + }) + ]]) + + feed('o') + screen:expect({ + grid = [[ + aaa | + ^ {19:NEW1} | + bbb | + ccc | + {1:~ }| + {5:-- INSERT --} | + ]], + }) + feed('<CR>') + screen:expect({ + grid = [[ + aaa | + {19:NEW1} | + ^ {19:NEW2} | + bbb | + ccc | + {5:-- INSERT --} | + ]], + }) + end) end) describe('lua: nvim_buf_attach on_bytes', function() @@ -426,14 +467,14 @@ describe('lua: nvim_buf_attach on_bytes', function() it('opening lines', function() local check_events = setup_eventcheck(verify, origlines) - -- api.nvim_set_option_value('autoindent', true, {}) + api.nvim_set_option_value('autoindent', false, {}) feed 'Go' check_events { { 'test1', 'bytes', 1, 3, 7, 0, 114, 0, 0, 0, 1, 0, 1 }, } feed '<cr>' check_events { - { 'test1', 'bytes', 1, 5, 7, 0, 114, 0, 0, 0, 1, 0, 1 }, + { 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 1 }, } end) @@ -447,7 +488,7 @@ describe('lua: nvim_buf_attach on_bytes', function() feed '<cr>' check_events { { 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 4, 4, 0, 0, 0 }, - { 'test1', 'bytes', 1, 5, 7, 0, 114, 0, 0, 0, 1, 4, 5 }, + { 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 0, 0, 1, 4, 5 }, } end) @@ -477,7 +518,7 @@ describe('lua: nvim_buf_attach on_bytes', function() api.nvim_set_option_value('filetype', 'c', {}) feed 'A<CR>' check_events { - { 'test1', 'bytes', 1, 4, 0, 10, 10, 0, 0, 0, 1, 3, 4 }, + { 'test1', 'bytes', 1, 3, 0, 10, 10, 0, 0, 0, 1, 3, 4 }, } feed '<ESC>' @@ -493,7 +534,7 @@ describe('lua: nvim_buf_attach on_bytes', function() feed '<CR>' check_events { { 'test1', 'bytes', 1, 6, 1, 2, 13, 0, 1, 1, 0, 0, 0 }, - { 'test1', 'bytes', 1, 7, 1, 2, 13, 0, 0, 0, 1, 3, 4 }, + { 'test1', 'bytes', 1, 6, 1, 2, 13, 0, 0, 0, 1, 3, 4 }, } end) diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index a5cdfdc225..6e7811908b 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -36,6 +36,7 @@ local test_basename_dirname_eq = { 'c:/users/foo', 'c:/users/foo/bar.lua', 'c:/users/foo/bar/../', + '~/foo/bar\\baz', } local tests_windows_paths = { @@ -70,26 +71,26 @@ describe('vim.fs', function() it('works', function() eq(test_build_dir, vim.fs.dirname(nvim_dir)) - --- @param paths string[] - local function test_paths(paths) + ---@param paths string[] + ---@param is_win? boolean + local function test_paths(paths, is_win) + local gsub = is_win and [[:gsub('\\', '/')]] or '' + local code = string.format( + [[ + local path = ... + return vim.fn.fnamemodify(path,':h')%s + ]], + gsub + ) + for _, path in ipairs(paths) do - eq( - exec_lua( - [[ - local path = ... - return vim.fn.fnamemodify(path,':h'):gsub('\\', '/') - ]], - path - ), - vim.fs.dirname(path), - path - ) + eq(exec_lua(code, path), vim.fs.dirname(path), path) end end test_paths(test_basename_dirname_eq) if is_os('win') then - test_paths(tests_windows_paths) + test_paths(tests_windows_paths, true) end end) end) @@ -98,26 +99,26 @@ describe('vim.fs', function() it('works', function() eq(nvim_prog_basename, vim.fs.basename(nvim_prog)) - --- @param paths string[] - local function test_paths(paths) + ---@param paths string[] + ---@param is_win? boolean + local function test_paths(paths, is_win) + local gsub = is_win and [[:gsub('\\', '/')]] or '' + local code = string.format( + [[ + local path = ... + return vim.fn.fnamemodify(path,':t')%s + ]], + gsub + ) + for _, path in ipairs(paths) do - eq( - exec_lua( - [[ - local path = ... - return vim.fn.fnamemodify(path,':t'):gsub('\\', '/') - ]], - path - ), - vim.fs.basename(path), - path - ) + eq(exec_lua(code, path), vim.fs.basename(path), path) end end test_paths(test_basename_dirname_eq) if is_os('win') then - test_paths(tests_windows_paths) + test_paths(tests_windows_paths, true) end end) end) @@ -284,9 +285,6 @@ describe('vim.fs', function() end) describe('normalize()', function() - it('works with backward slashes', function() - eq('C:/Users/jdoe', vim.fs.normalize('C:\\Users\\jdoe')) - end) it('removes trailing /', function() eq('/home/user', vim.fs.normalize('/home/user/')) end) @@ -309,10 +307,27 @@ describe('vim.fs', function() ) ) end) + + it('works with UNC paths', function() + eq('//foo', vim.fs.normalize('//foo')) -- UNC path + eq('//foo/bar', vim.fs.normalize('//foo//bar////')) -- UNC path + eq('/foo', vim.fs.normalize('///foo')) -- Not a UNC path + eq('/', vim.fs.normalize('//')) -- Not a UNC path + eq('/', vim.fs.normalize('///')) -- Not a UNC path + eq('/foo/bar', vim.fs.normalize('/foo//bar////')) -- Not a UNC path + end) + if is_os('win') then it('Last slash is not truncated from root drive', function() eq('C:/', vim.fs.normalize('C:/')) end) + it('converts backward slashes', function() + eq('C:/Users/jdoe', vim.fs.normalize('C:\\Users\\jdoe')) + end) + else + it('allows backslashes on unix-based os', function() + eq('/home/user/hello\\world', vim.fs.normalize('/home/user/hello\\world')) + end) end end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 7ab009659b..8722c4dbb5 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -3007,25 +3007,65 @@ describe('lua stdlib', function() end) describe('vim.on_key', function() - it('tracks keystrokes', function() + it('tracks Unicode input', function() insert([[hello world ]]) exec_lua [[ keys = {} + typed = {} - vim.on_key(function(buf) + vim.on_key(function(buf, typed_buf) if buf:byte() == 27 then buf = "<ESC>" end + if typed_buf:byte() == 27 then + typed_buf = "<ESC>" + end table.insert(keys, buf) + table.insert(typed, typed_buf) end) ]] - insert([[next 🤦 lines å ]]) + insert([[next 🤦 lines å …]]) -- It has escape in the keys pressed - eq('inext 🤦 lines å <ESC>', exec_lua [[return table.concat(keys, '')]]) + eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(keys, '')]]) + eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(typed, '')]]) + end) + + it('tracks input with modifiers', function() + exec_lua [[ + keys = {} + typed = {} + + vim.on_key(function(buf, typed_buf) + table.insert(keys, vim.fn.keytrans(buf)) + table.insert(typed, vim.fn.keytrans(typed_buf)) + end) + ]] + + feed([[i<C-V><C-;><C-V><C-…><Esc>]]) + + eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(keys, '')]]) + eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(typed, '')]]) + end) + + it('works with character find and Select mode', function() + insert('12345') + + exec_lua [[ + typed = {} + + vim.cmd('snoremap # @') + + vim.on_key(function(buf, typed_buf) + table.insert(typed, vim.fn.keytrans(typed_buf)) + end) + ]] + + feed('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^') + eq('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^', exec_lua [[return table.concat(typed, '')]]) end) it('allows removing on_key listeners', function() @@ -3087,23 +3127,29 @@ describe('lua stdlib', function() eq('inext l', exec_lua [[ return table.concat(keys, '') ]]) end) - it('processes mapped keys, not unmapped keys', function() + it('argument 1 is keys after mapping, argument 2 is typed keys', function() exec_lua [[ keys = {} + typed = {} vim.cmd("inoremap hello world") - vim.on_key(function(buf) + vim.on_key(function(buf, typed_buf) if buf:byte() == 27 then buf = "<ESC>" end + if typed_buf:byte() == 27 then + typed_buf = "<ESC>" + end table.insert(keys, buf) + table.insert(typed, typed_buf) end) ]] insert('hello') eq('iworld<ESC>', exec_lua [[return table.concat(keys, '')]]) + eq('ihello<ESC>', exec_lua [[return table.concat(typed, '')]]) end) it('can call vim.fn functions on Ctrl-C #17273', function() diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index 0395d5ee16..08ec7b97cf 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -41,6 +41,7 @@ describe(':terminal mouse', function() end) it('will exit focus and trigger Normal mode mapping on mouse click', function() + feed([[<C-\><C-N>qri]]) command('let g:got_leftmouse = 0') command('nnoremap <LeftMouse> <Cmd>let g:got_leftmouse = 1<CR>') eq('t', eval('mode(1)')) @@ -48,9 +49,12 @@ describe(':terminal mouse', function() feed('<LeftMouse>') eq('nt', eval('mode(1)')) eq(1, eval('g:got_leftmouse')) + feed('q') + eq('i<LeftMouse>', eval('keytrans(@r)')) end) it('will exit focus and trigger Normal mode mapping on mouse click with modifier', function() + feed([[<C-\><C-N>qri]]) command('let g:got_ctrl_leftmouse = 0') command('nnoremap <C-LeftMouse> <Cmd>let g:got_ctrl_leftmouse = 1<CR>') eq('t', eval('mode(1)')) @@ -58,6 +62,8 @@ describe(':terminal mouse', function() feed('<C-LeftMouse>') eq('nt', eval('mode(1)')) eq(1, eval('g:got_ctrl_leftmouse')) + feed('q') + eq('i<C-LeftMouse>', eval('keytrans(@r)')) end) it('will exit focus on <C-\\> + mouse-scroll', function() diff --git a/test/functional/vimscript/reltime_spec.lua b/test/functional/vimscript/reltime_spec.lua index 7cdb78e4ce..fc4dd0d87b 100644 --- a/test/functional/vimscript/reltime_spec.lua +++ b/test/functional/vimscript/reltime_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok local neq, command, fn = helpers.neq, helpers.command, helpers.fn +local matches = helpers.matches local reltime, reltimestr, reltimefloat = fn.reltime, fn.reltimestr, fn.reltimefloat describe('reltimestr(), reltimefloat()', function() @@ -15,7 +16,7 @@ describe('reltimestr(), reltimefloat()', function() neq('0.0', reltimestr(elapsed)) ok(reltimefloat(elapsed) > 0.0) -- original vim test for < 0.1, but easily fails on travis - ok(nil ~= string.match(reltimestr(elapsed), '0%.')) + matches('0%.', reltimestr(elapsed)) ok(reltimefloat(elapsed) < 1.0) local same = reltime(now, now) @@ -29,7 +30,7 @@ describe('reltimestr(), reltimefloat()', function() neq('0.0', reltimestr(differs)) ok(reltimefloat(differs) > 0.0) -- original vim test for < 0.1, but easily fails on travis - ok(nil ~= string.match(reltimestr(differs), '0%.')) + matches('0%.', reltimestr(differs)) ok(reltimefloat(differs) < 1.0) end) diff --git a/test/functional/vimscript/server_spec.lua b/test/functional/vimscript/server_spec.lua index 360fcf0dfe..c6c9e9c0ab 100644 --- a/test/functional/vimscript/server_spec.lua +++ b/test/functional/vimscript/server_spec.lua @@ -100,14 +100,14 @@ describe('server', function() local s = fn.serverstart('127.0.0.1:0') -- assign random port if #s > 0 then - assert(string.match(s, '127.0.0.1:%d+')) + matches('127.0.0.1:%d+', s) eq(s, fn.serverlist()[1]) clear_serverlist() end s = fn.serverstart('127.0.0.1:') -- assign random port if #s > 0 then - assert(string.match(s, '127.0.0.1:%d+')) + matches('127.0.0.1:%d+', s) eq(s, fn.serverlist()[1]) clear_serverlist() end diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim index f1040b4b35..61de6775fb 100644 --- a/test/old/testdir/test_autocmd.vim +++ b/test/old/testdir/test_autocmd.vim @@ -3957,4 +3957,101 @@ func Test_implicit_session() call delete(expected) endfunc +" Test TextChangedI and TextChanged +func Test_Changed_ChangedI_2() + CheckRunVimInTerminal + call writefile(['one', 'two', 'three'], 'XTextChangedI2', 'D') + let before =<< trim END + autocmd TextChanged,TextChangedI * call writefile([b:changedtick], 'XTextChangedI3') + nnoremap <CR> o<Esc> + call writefile([], 'XTextChangedI3') + END + + call writefile(before, 'Xinit', 'D') + let buf = RunVimInTerminal('-S Xinit XtextChangedI2', {}) + call term_sendkeys(buf, "\<cr>") + call term_wait(buf) + call StopVimInTerminal(buf) + call assert_equal(['4'], readfile('XTextChangedI3')) + + call delete('XTextChangedI3') +endfunc + +" Test that filetype detection still works when SwapExists autocommand sets +" filetype in another buffer. +func Test_SwapExists_set_other_buf_filetype() + let lines =<< trim END + set nocompatible directory=. + filetype on + + let g:buf = bufnr() + new + + func SwapExists() + let v:swapchoice = 'o' + call setbufvar(g:buf, '&filetype', 'text') + endfunc + + func SafeState() + edit <script> + redir! > XftSwapExists.out + set readonly? filetype? + redir END + qall! + endfunc + + autocmd SwapExists * ++nested call SwapExists() + autocmd SafeState * ++nested ++once call SafeState() + END + call writefile(lines, 'XftSwapExists.vim', 'D') + + new XftSwapExists.vim + if RunVim('', '', ' -S XftSwapExists.vim') + call assert_equal( + \ ['', ' readonly', ' filetype=vim'], + \ readfile('XftSwapExists.out')) + call delete('XftSwapExists.out') + endif + + bwipe! +endfunc + +" Test that file is not marked as modified when SwapExists autocommand sets +" 'modified' in another buffer. +func Test_SwapExists_set_other_buf_modified() + let lines =<< trim END + set nocompatible directory=. + + let g:buf = bufnr() + new + + func SwapExists() + let v:swapchoice = 'o' + call setbufvar(g:buf, '&modified', 1) + endfunc + + func SafeState() + edit <script> + redir! > XmodSwapExists.out + set readonly? modified? + redir END + qall! + endfunc + + autocmd SwapExists * ++nested call SwapExists() + autocmd SafeState * ++nested ++once call SafeState() + END + call writefile(lines, 'XmodSwapExists.vim', 'D') + + new XmodSwapExists.vim + if RunVim('', '', ' -S XmodSwapExists.vim') + call assert_equal( + \ ['', ' readonly', 'nomodified'], + \ readfile('XmodSwapExists.out')) + call delete('XmodSwapExists.out') + endif + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_conceal.vim b/test/old/testdir/test_conceal.vim index 84b931f39d..df20654f12 100644 --- a/test/old/testdir/test_conceal.vim +++ b/test/old/testdir/test_conceal.vim @@ -197,6 +197,7 @@ endfunc " Same as Test_conceal_wrapped_cursorline_wincolor(), but with 'rightleft'. func Test_conceal_wrapped_cursorline_wincolor_rightleft() + CheckFeature rightleft CheckScreendump let code =<< trim [CODE] diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index 937f18aa69..26270fddd2 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -4184,21 +4184,29 @@ func Test_normal34_zet_large() norm! z9765405999999999999 endfunc -" Test for { and } paragraph movements and Ctrl-B in buffer with a single line -func Test_single_line_scroll() - CheckFeature textprop - +" Test for { and } paragraph movements in a single line +func Test_brace_single_line() new call setline(1, ['foobar one two three']) - let vt = 'virt_above' - call prop_type_add(vt, {'highlight': 'IncSearch'}) - call prop_add(1, 0, {'type': vt, 'text': '---', 'text_align': 'above'}) 1 norm! 0} call assert_equal([0, 1, 20, 0], getpos('.')) norm! { call assert_equal([0, 1, 1, 0], getpos('.')) + bw! +endfunc + +" Test for Ctrl-B/Ctrl-U in buffer with a single line +func Test_single_line_scroll() + CheckFeature textprop + + new + call setline(1, ['foobar one two three']) + let vt = 'virt_above' + call prop_type_add(vt, {'highlight': 'IncSearch'}) + call prop_add(1, 0, {'type': vt, 'text': '---', 'text_align': 'above'}) + call cursor(1, 1) " Ctrl-B/Ctrl-U scroll up with hidden "above" virtual text. set smoothscroll @@ -4213,6 +4221,7 @@ func Test_single_line_scroll() set smoothscroll& bw! + call prop_type_delete(vt) endfunc " vim: shiftwidth=2 sts=2 expandtab nofoldenable diff --git a/test/old/testdir/test_perl.vim b/test/old/testdir/test_perl.vim index 37b1d08b1a..c08a042dae 100644 --- a/test/old/testdir/test_perl.vim +++ b/test/old/testdir/test_perl.vim @@ -94,6 +94,7 @@ func Test_buffer_Number() endfunc func Test_window_Cursor() + throw 'skipped: flaky ' new call setline(1, ['line1', 'line2']) perl $curwin->Cursor(2, 3) @@ -104,7 +105,7 @@ func Test_window_Cursor() endfunc func Test_window_SetHeight() - throw 'skipped: very flaky ' + throw 'skipped: flaky ' new perl $curwin->SetHeight(2) call assert_equal(2, winheight(0)) diff --git a/test/old/testdir/test_registers.vim b/test/old/testdir/test_registers.vim index e113bd9e75..1a25844ad3 100644 --- a/test/old/testdir/test_registers.vim +++ b/test/old/testdir/test_registers.vim @@ -214,6 +214,58 @@ func Test_recording_with_select_mode() bwipe! endfunc +func Run_test_recording_with_select_mode_utf8() + new + + " Test with different text lengths: 5, 7, 9, 11, 13, 15, to check that + " a character isn't split between two buffer blocks. + for s in ['12345', '口=口', '口口口', '口=口=口', '口口=口口', '口口口口口'] + " 0x80 is K_SPECIAL + " 0x9B is CSI + " 哦: 0xE5 0x93 0xA6 + " 洛: 0xE6 0xB4 0x9B + " 固: 0xE5 0x9B 0xBA + " 四: 0xE5 0x9B 0x9B + " 最: 0xE6 0x9C 0x80 + " 倒: 0xE5 0x80 0x92 + " 倀: 0xE5 0x80 0x80 + for c in ['哦', '洛', '固', '四', '最', '倒', '倀'] + call setline(1, 'asdf') + call feedkeys($"qacc{s}\<Esc>gH{c}\<Esc>q", "tx") + call assert_equal(c, getline(1)) + call assert_equal($"cc{s}\<Esc>gH{c}\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal(c, getline(1)) + + " Test with Shift modifier. + let shift_c = eval($'"\<S-{c}>"') + call setline(1, 'asdf') + call feedkeys($"qacc{s}\<Esc>gH{shift_c}\<Esc>q", "tx") + call assert_equal(c, getline(1)) + call assert_equal($"cc{s}\<Esc>gH{shift_c}\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal(c, getline(1)) + endfor + endfor + + bwipe! +endfunc + +func Test_recording_with_select_mode_utf8() + call Run_test_recording_with_select_mode_utf8() +endfunc + +" This must be done as one of the last tests, because it starts the GUI, which +" cannot be undone. +func Test_zz_recording_with_select_mode_utf8_gui() + CheckCanRunGui + + gui -f + call Run_test_recording_with_select_mode_utf8() +endfunc + " Test for executing the last used register (@) func Test_last_used_exec_reg() " Test for the @: command diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index fbc714d9cc..575ce0c6b5 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -741,6 +741,7 @@ func Test_smoothscroll_mouse_pos() let &mouse = save_mouse "let &term = save_term "let &ttymouse = save_ttymouse + bwipe! endfunc " this was dividing by zero @@ -1002,9 +1003,8 @@ func Test_smoothscroll_textoff_small_winwidth() endfunc func Test_smoothscroll_page() - set smoothscroll - - 10split | 40vsplit + call NewWindow(10, 40) + setlocal smoothscroll call setline(1, 'abcde '->repeat(150)) exe "norm! \<C-F>" @@ -1041,7 +1041,7 @@ func Test_smoothscroll_page() exe "norm! \<C-U>" call assert_equal(0, winsaveview().skipcol) - set smoothscroll& + bwipe! endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_utf8.vim b/test/old/testdir/test_utf8.vim index a5a9624ec3..920000e2c7 100644 --- a/test/old/testdir/test_utf8.vim +++ b/test/old/testdir/test_utf8.vim @@ -292,55 +292,4 @@ func Test_print_overlong() bwipe! endfunc -func Test_recording_with_select_mode_utf8() - call Run_test_recording_with_select_mode_utf8() -endfunc - -func Run_test_recording_with_select_mode_utf8() - new - - " No escaping - call feedkeys("qacc12345\<Esc>gH哦\<Esc>q", "tx") - call assert_equal("哦", getline(1)) - call assert_equal("cc12345\<Esc>gH哦\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("哦", getline(1)) - - " 固 is 0xE5 0x9B 0xBA where 0x9B is CSI - call feedkeys("qacc12345\<Esc>gH固\<Esc>q", "tx") - call assert_equal("固", getline(1)) - call assert_equal("cc12345\<Esc>gH固\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("固", getline(1)) - - " 四 is 0xE5 0x9B 0x9B where 0x9B is CSI - call feedkeys("qacc12345\<Esc>gH四\<Esc>q", "tx") - call assert_equal("四", getline(1)) - call assert_equal("cc12345\<Esc>gH四\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("四", getline(1)) - - " 倒 is 0xE5 0x80 0x92 where 0x80 is K_SPECIAL - call feedkeys("qacc12345\<Esc>gH倒\<Esc>q", "tx") - call assert_equal("倒", getline(1)) - call assert_equal("cc12345\<Esc>gH倒\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("倒", getline(1)) - - bwipe! -endfunc - -" This must be done as one of the last tests, because it starts the GUI, which -" cannot be undone. -func Test_zz_recording_with_select_mode_utf8_gui() - CheckCanRunGui - - gui -f - call Run_test_recording_with_select_mode_utf8() -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 1cd5647a9e..a775bb48b4 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -3223,7 +3223,7 @@ describe('typval.c', function() end) end) describe('lnum()', function() - itp('works', function() + pending('works (skip due to flakiness)', function() for _, v in ipairs({ { lib.VAR_NUMBER, { v_number = 42 }, nil, 42 }, { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, 100500 }, |