diff options
329 files changed, 13023 insertions, 8769 deletions
diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml index 413c2e90c6..ee72cf5f01 100644 --- a/.github/workflows/api-docs.yml +++ b/.github/workflows/api-docs.yml @@ -26,7 +26,9 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y doxygen python3 python3-msgpack luajit + sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y python3 luajit + conda install -c conda-forge doxygen=1.9.2 msgpack-python + echo "$CONDA/bin" >> $GITHUB_PATH - name: Setup git config run: | diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 25f8414008..d281bb1404 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -12,7 +12,7 @@ jobs: name: Backport Pull Request if: > github.repository_owner == 'neovim' && ( - github.event_name == 'pull_request' && + github.event_name == 'pull_request_target' && github.event.pull_request.merged ) || ( github.event_name == 'issue_comment' && diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d07b9fdac7..6a81ee4238 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,6 @@ jobs: # Workaround brew issues rm -f /usr/local/bin/2to3 brew update >/dev/null - brew upgrade brew install automake ccache perl cpanminus ninja - name: Setup interpreter packages diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 559d8eae83..6c74ec99d3 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -9,7 +9,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v2.3.1 + - uses: actions/checkout@v2 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f1ed05e6cb..5839be2944 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,7 +82,6 @@ jobs: run: | rm -f /usr/local/bin/2to3 brew update >/dev/null - brew upgrade brew install automake ninja - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV diff --git a/.gitignore b/.gitignore index e07ce4906e..0988a51cd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Tools /venv/ compile_commands.json +/.luarc.json # IDEs /.vs/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f44937b5ae..2c2587ec0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,12 +136,12 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY # If not in a git repo (e.g., a tarball) these tokens define the complete # version string, else they are combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) -set(NVIM_VERSION_MINOR 6) +set(NVIM_VERSION_MINOR 7) set(NVIM_VERSION_PATCH 0) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level -set(NVIM_API_LEVEL 8) # Bump this after any API change. +set(NVIM_API_LEVEL 9) # Bump this after any API change. set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. set(NVIM_API_PRERELEASE true) @@ -543,6 +543,24 @@ endif() message(STATUS "Using Lua interpreter: ${LUA_PRG}") +if(NOT WIN32) + if(PREFER_LUA) + foreach(CURRENT_LUAC_PRG luac5.1 luac) + find_program(_CHECK_LUAC_PRG ${CURRENT_LUAC_PRG}) + if(_CHECK_LUAC_PRG) + set(LUAC_PRG "${_CHECK_LUAC_PRG} -s -o - %s" CACHE STRING "Format for compiling to Lua bytecode") + break() + endif() + endforeach() + elseif(LUA_PRG MATCHES "luajit") + set(LUAC_PRG "${LUA_PRG} -b -s %s -" CACHE STRING "Format for compiling to Lua bytecode") + endif() +endif() + +if(LUAC_PRG) + message(STATUS "Using Lua compiler: ${LUAC_PRG}") +endif() + # Setup busted. find_program(BUSTED_PRG NAMES busted busted.bat) find_program(BUSTED_LUA_PRG busted-lua) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9c1173007..faf9181a2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -244,6 +244,10 @@ You can lint a single file (but this will _not_ exclude legacy errors): ("Exuberant ctags", the typical `ctags` binary provided by your distro, is unmaintained and won't recognize many function signatures in Neovim source.) - Explore the source code [on the web](https://sourcegraph.com/github.com/neovim/neovim). +- If using [lua-language-server][], symlink `contrib/luarc.json` into the + project root: + + $ ln -s contrib/luarc.json .luarc.json Reviewing @@ -252,10 +256,10 @@ Reviewing To help review pull requests, start with [this checklist][review-checklist]. Reviewing can be done on GitHub, but you may find it easier to do locally. -Using [`hub`][hub], you can create a new branch with the contents of a pull +Using [GitHub CLI][gh], you can create a new branch with the contents of a pull request, e.g. [#1820][1820]: - hub checkout https://github.com/neovim/neovim/pull/1820 + gh pr checkout https://github.com/neovim/neovim/pull/1820 Use [`git log -p master..FETCH_HEAD`][git-history-filtering] to list all commits in the feature branch which aren't in the `master` branch; `-p` @@ -270,7 +274,7 @@ as context, use the `-W` argument as well. [git-rebasing]: http://git-scm.com/book/en/v2/Git-Branching-Rebasing [github-issues]: https://github.com/neovim/neovim/issues [1820]: https://github.com/neovim/neovim/pull/1820 -[hub]: https://hub.github.com/ +[gh]: https://cli.github.com/ [conventional_commits]: https://www.conventionalcommits.org [style-guide]: https://neovim.io/doc/user/dev_style.html#dev-style [ASan]: http://clang.llvm.org/docs/AddressSanitizer.html @@ -287,4 +291,5 @@ as context, use the `-W` argument as well. [wiki-contribute-help]: https://github.com/neovim/neovim/wiki/contribute-%3Ahelp [pr-draft]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request [pr-ready]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request -[uncrustify]: https://formulae.brew.sh/formula/uncrustify +[uncrustify]: http://uncrustify.sourceforge.net/ +[lua-language-server]: https://github.com/sumneko/lua-language-server/ diff --git a/ci/snap/deploy.sh b/ci/snap/deploy.sh index 579c48e933..8429059e22 100755 --- a/ci/snap/deploy.sh +++ b/ci/snap/deploy.sh @@ -24,7 +24,7 @@ snap_realease_needed() { trigger_snapcraft_webhook() { [[ -n "${PAYLOAD_SIG}" ]] || exit - echo "Triggering new snap relase via webhook..." + echo "Triggering new snap release via webhook..." curl -X POST \ -H "Content-Type: application/json" \ -H "X-Hub-Signature: sha1=${PAYLOAD_SIG}" \ diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index e78392562f..3adbcbbfc5 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -46,6 +46,10 @@ if(DEFINED ENV{TEST_FILTER} AND NOT "$ENV{TEST_FILTER}" STREQUAL "") list(APPEND BUSTED_ARGS --filter $ENV{TEST_FILTER}) endif() +if(DEFINED ENV{TEST_FILTER_OUT} AND NOT "$ENV{TEST_FILTER_OUT}" STREQUAL "") + list(APPEND BUSTED_ARGS --filter-out $ENV{TEST_FILTER_OUT}) +endif() + # TMPDIR: use relative test path (for parallel test runs / isolation). set(ENV{TMPDIR} "${BUILD_DIR}/Xtest_tmpdir/${TEST_PATH}") execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory $ENV{TMPDIR}) diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index 581f25857b..8f93e1eb27 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -45,10 +45,24 @@ check_function_exists(readlink HAVE_READLINK) check_function_exists(setpgid HAVE_SETPGID) check_function_exists(setsid HAVE_SETSID) check_function_exists(sigaction HAVE_SIGACTION) +check_function_exists(strnlen HAVE_STRNLEN) check_function_exists(strcasecmp HAVE_STRCASECMP) check_function_exists(strncasecmp HAVE_STRNCASECMP) check_function_exists(strptime HAVE_STRPTIME) +if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + check_c_source_compiles(" +#include <termios.h> +int +main(void) +{ + return forkpty(0, NULL, NULL, NULL); +} +" HAVE_FORKPTY) +else() + set(HAVE_FORKPTY 1) +endif() + # Symbols check_symbol_exists(FD_CLOEXEC "fcntl.h" HAVE_FD_CLOEXEC) if(HAVE_LANGINFO_H) @@ -92,7 +106,7 @@ if (NOT "${HAVE_BE64TOH}") # any case and ORDER_BIG_ENDIAN will not be examined. # - CMAKE_CROSSCOMPILING *and* HAVE_BE64TOH are both false. In this case # be64toh function which uses cycle and arithmetic operations is used which - # will work regardless of endianess. Function is sub-optimal though. + # will work regardless of endianness. Function is sub-optimal though. check_c_source_runs(" ${SI} ${MS} diff --git a/config/config.h.in b/config/config.h.in index 27a28116af..b44b7238d2 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -30,6 +30,7 @@ #cmakedefine HAVE_SETPGID #cmakedefine HAVE_SETSID #cmakedefine HAVE_SIGACTION +#cmakedefine HAVE_STRNLEN #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE_STRINGS_H #cmakedefine HAVE_STRNCASECMP @@ -49,6 +50,7 @@ # undef HAVE_SYS_UIO_H # endif #endif +#cmakedefine HAVE_FORKPTY #cmakedefine FEAT_TUI diff --git a/contrib/local.mk.example b/contrib/local.mk.example index 778e848d60..20396e86ae 100644 --- a/contrib/local.mk.example +++ b/contrib/local.mk.example @@ -27,7 +27,7 @@ # With non-Debug builds interprocedural optimization (IPO) (which includes # link-time optimization (LTO)) is enabled by default, which causes the link -# step to take a significant amout of time, which is relevant when building +# step to take a significant amount of time, which is relevant when building # often. You can disable it explicitly: # CMAKE_EXTRA_FLAGS += -DENABLE_LTO=OFF diff --git a/contrib/luarc.json b/contrib/luarc.json new file mode 100644 index 0000000000..770b023ac6 --- /dev/null +++ b/contrib/luarc.json @@ -0,0 +1,23 @@ +{ + "runtime.version": "LuaJIT", + "diagnostics": { + "enable": true, + "globals": [ + "vim", + "describe", + "it", + "before_each", + "after_each", + "setup", + "teardown" + ] + }, + "workspace": { + "library": { + "runtime/lua": true + }, + "maxPreload": 2000, + "preloadFileSize": 1000 + }, + "telemetry.enable": false +} diff --git a/man/nvim.1 b/man/nvim.1 index b206b62343..43dfc21dc7 100644 --- a/man/nvim.1 +++ b/man/nvim.1 @@ -177,8 +177,7 @@ If .Ar vimrc is .Cm NORC , -do not load any initialization files (except plugins), -and do not attempt to parse environment variables. +do not load any initialization files (except plugins). If .Ar vimrc is diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 37029874f2..f656f1cbc3 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -123,7 +123,7 @@ foreach(PROG ${RUNTIME_PROGRAMS}) endforeach() globrecurse_wrapper(RUNTIME_FILES ${CMAKE_CURRENT_SOURCE_DIR} - rgb.txt *.vim *.lua *.dict *.py *.rb *.ps *.spl *.tutor *.tutor.json) + *.vim *.lua *.dict *.py *.rb *.ps *.spl *.tutor *.tutor.json) foreach(F ${RUNTIME_FILES}) get_filename_component(BASEDIR ${F} PATH) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 7484149a26..366d7b009d 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -1,7 +1,7 @@ " Vim functions for file type detection " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2020 Aug 17 +" Last Change: 2021 Nov 27 " These functions are moved here from runtime/filetype.vim to make startup " faster. @@ -219,6 +219,23 @@ func dist#ft#FTe() endif endfunc +" Distinguish between Forth and F#. +" Provided by Doug Kearns. +func dist#ft#FTfs() + if exists("g:filetype_fs") + exe "setf " . g:filetype_fs + else + let line = getline(nextnonblank(1)) + " comments and colon definitions + if line =~ '^\s*\.\=( ' || line =~ '^\s*\\G\= ' || line =~ '^\\$' + \ || line =~ '^\s*: \S' + setf forth + else + setf fsharp + endif + endif +endfunc + " Distinguish between HTML, XHTML and Django func dist#ft#FThtml() let n = 1 @@ -272,6 +289,8 @@ func dist#ft#FTm() " excluding end(for|function|if|switch|while) common to Murphi let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>' + let objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>' + let n = 1 let saw_comment = 0 " Whether we've seen a multiline comment leader. while n < 100 @@ -282,7 +301,7 @@ func dist#ft#FTm() " anything more definitive. let saw_comment = 1 endif - if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|//\)' + if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor setf objc return endif @@ -792,6 +811,23 @@ func dist#ft#Redif() endwhile endfunc +" This function is called for all files under */debian/patches/*, make sure not +" to non-dep3patch files, such as README and other text files. +func dist#ft#Dep3patch() + if expand('%:t') ==# 'series' + return + endif + + for ln in getline(1, 100) + if ln =~# '^\%(Description\|Subject\|Origin\|Bug\|Forwarded\|Author\|From\|Reviewed-by\|Acked-by\|Last-Updated\|Applied-Upstream\):' + setf dep3patch + return + elseif ln =~# '^---' + " end of headers found. stop processing + return + endif + endfor +endfunc " Restore 'cpoptions' let &cpo = s:cpo_save diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index 73c1459f86..1d462ad02c 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -1,27 +1,3 @@ -function! s:enhance_syntax() abort - syntax case match - - syntax keyword healthError ERROR[:] - \ containedin=markdownCodeBlock,mkdListItemLine - highlight default link healthError Error - - syntax keyword healthWarning WARNING[:] - \ containedin=markdownCodeBlock,mkdListItemLine - highlight default link healthWarning WarningMsg - - syntax keyword healthSuccess OK[:] - \ containedin=markdownCodeBlock,mkdListItemLine - highlight default healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232 - - syntax match healthHelp "|.\{-}|" contains=healthBar - \ containedin=markdownCodeBlock,mkdListItemLine - syntax match healthBar "|" contained conceal - highlight default link healthHelp Identifier - - " We do not care about markdown syntax errors in :checkhealth output. - highlight! link markdownError Normal -endfunction - " Runs the specified healthchecks. " Runs all discovered healthchecks if a:plugin_names is empty. function! health#check(plugin_names) abort @@ -29,13 +5,9 @@ function! health#check(plugin_names) abort \ ? s:discover_healthchecks() \ : s:get_healthcheck(a:plugin_names) - tabnew - setlocal wrap breakindent linebreak - setlocal filetype=markdown - setlocal conceallevel=2 concealcursor=nc - setlocal keywordprg=:help - let &l:iskeyword='!-~,^*,^|,^",192-255' - call s:enhance_syntax() + " create scratch-buffer + execute 'tab sbuffer' nvim_create_buf(v:true, v:true) + setfiletype checkhealth if empty(healthchecks) call setline(1, 'ERROR: No healthchecks found.') @@ -70,8 +42,6 @@ function! health#check(plugin_names) abort " needed for plasticboy/vim-markdown, because it uses fdm=expr normal! zR - setlocal nomodified - setlocal bufhidden=hide redraw|echo '' endfunction diff --git a/runtime/autoload/remote/define.vim b/runtime/autoload/remote/define.vim index 2aec96e365..82e5164d85 100644 --- a/runtime/autoload/remote/define.vim +++ b/runtime/autoload/remote/define.vim @@ -240,7 +240,11 @@ function! s:GetAutocmdPrefix(name, opts) endif if has_key(a:opts, 'nested') && a:opts.nested - call add(rv, 'nested') + call add(rv, '++nested') + endif + + if has_key(a:opts, 'once') && a:opts.once + call add(rv, '++once') endif return join(rv, ' ') diff --git a/runtime/autoload/zip.vim b/runtime/autoload/zip.vim index 9588bbf4a2..bc9b62ddb0 100644 --- a/runtime/autoload/zip.vim +++ b/runtime/autoload/zip.vim @@ -1,7 +1,7 @@ " zip.vim: Handles browsing zipfiles " AUTOLOAD PORTION -" Date: Jan 07, 2020 -" Version: 31 +" Date: Nov 08, 2021 +" Version: 32 " Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " License: Vim License (see vim's :help license) " Copyright: Copyright (C) 2005-2019 Charles E. Campbell {{{1 @@ -20,7 +20,7 @@ if &cp || exists("g:loaded_zip") finish endif -let g:loaded_zip= "v31" +let g:loaded_zip= "v32" if v:version < 702 echohl WarningMsg echo "***warning*** this version of zip needs vim 7.2 or later" @@ -115,7 +115,13 @@ fun! zip#Browse(zipfile) setlocal bufhidden=hide setlocal nobuflisted setlocal nowrap - set ft=tar + + " Oct 12, 2021: need to re-use Bram's syntax/tar.vim. + " Setting the filetype to zip doesn't do anything (currently), + " but it is perhaps less confusing to curious perusers who do + " a :echo &ft + setf zip + run! syntax/tar.vim " give header call append(0, ['" zip.vim version '.g:loaded_zip, @@ -187,8 +193,8 @@ fun! s:ZipBrowseSelect() wincmd _ endif let s:zipfile_{winnr()}= curfile -" call Decho("exe e ".fnameescape("zipfile:".zipfile.'::'.fname)) - exe "noswapfile e ".fnameescape("zipfile:".zipfile.'::'.fname) +" call Decho("exe e ".fnameescape("zipfile://".zipfile.'::'.fname)) + exe "noswapfile e ".fnameescape("zipfile://".zipfile.'::'.fname) filetype detect let &report= repkeep @@ -203,11 +209,11 @@ fun! zip#Read(fname,mode) set report=10 if has("unix") - let zipfile = substitute(a:fname,'zipfile:\(.\{-}\)::[^\\].*$','\1','') - let fname = substitute(a:fname,'zipfile:.\{-}::\([^\\].*\)$','\1','') + let zipfile = substitute(a:fname,'zipfile://\(.\{-}\)::[^\\].*$','\1','') + let fname = substitute(a:fname,'zipfile://.\{-}::\([^\\].*\)$','\1','') else - let zipfile = substitute(a:fname,'^.\{-}zipfile:\(.\{-}\)::[^\\].*$','\1','') - let fname = substitute(a:fname,'^.\{-}zipfile:.\{-}::\([^\\].*\)$','\1','') + let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','') + let fname = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','') let fname = substitute(fname, '[', '[[]', 'g') endif " call Decho("zipfile<".zipfile.">") @@ -224,7 +230,7 @@ fun! zip#Read(fname,mode) " the following code does much the same thing as " exe "keepj sil! r! ".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1) - " but allows zipfile:... entries in quickfix lists + " but allows zipfile://... entries in quickfix lists let temp = tempname() " call Decho("using temp file<".temp.">") let fn = expand('%:p') @@ -296,11 +302,11 @@ fun! zip#Write(fname) " call Decho("current directory now: ".getcwd()) if has("unix") - let zipfile = substitute(a:fname,'zipfile:\(.\{-}\)::[^\\].*$','\1','') - let fname = substitute(a:fname,'zipfile:.\{-}::\([^\\].*\)$','\1','') + let zipfile = substitute(a:fname,'zipfile://\(.\{-}\)::[^\\].*$','\1','') + let fname = substitute(a:fname,'zipfile://.\{-}::\([^\\].*\)$','\1','') else - let zipfile = substitute(a:fname,'^.\{-}zipfile:\(.\{-}\)::[^\\].*$','\1','') - let fname = substitute(a:fname,'^.\{-}zipfile:.\{-}::\([^\\].*\)$','\1','') + let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','') + let fname = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','') endif " call Decho("zipfile<".zipfile.">") " call Decho("fname <".fname.">") diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index bb8e83f84a..8fb6290e50 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -652,35 +652,6 @@ nvim_call_atomic({calls}) *nvim_call_atomic()* occurred, the values from all preceding calls will still be returned. - *nvim_call_dict_function()* -nvim_call_dict_function({dict}, {fn}, {args}) - Calls a VimL |Dictionary-function| with the given arguments. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {dict} Dictionary, or String evaluating to a VimL |self| - dict - {fn} Name of the function defined on the VimL dict - {args} Function arguments packed in an Array - - Return: ~ - Result of the function call - -nvim_call_function({fn}, {args}) *nvim_call_function()* - Calls a VimL function with the given arguments. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {fn} Function to call - {args} Function arguments packed in an Array - - Return: ~ - Result of the function call - nvim_chan_send({chan}, {data}) *nvim_chan_send()* Send data to channel `id` . For a job, it writes it to the stdin of the process. For the stdio channel |channel-stdio|, @@ -697,18 +668,6 @@ nvim_chan_send({chan}, {data}) *nvim_chan_send()* {chan} id of the channel {data} data to write. 8-bit clean: can contain NUL bytes. -nvim_command({command}) *nvim_command()* - Executes an ex-command. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {command} Ex-command string - - See also: ~ - |nvim_exec()| - nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* Creates a new, empty, unnamed buffer. @@ -724,22 +683,6 @@ nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* See also: ~ buf_open_scratch -nvim_create_namespace({name}) *nvim_create_namespace()* - Creates a new *namespace* or gets an existing one. - - Namespaces are used for buffer highlights and virtual text, - see |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. - - Namespaces can be named or anonymous. If `name` matches an - existing namespace, the associated id is returned. If `name` - is an empty string a new, anonymous namespace is created. - - Parameters: ~ - {name} Namespace name or empty string - - Return: ~ - Namespace id - nvim_del_current_line() *nvim_del_current_line()* Deletes the current line. @@ -806,19 +749,6 @@ nvim_err_writeln({str}) *nvim_err_writeln()* See also: ~ nvim_err_write() -nvim_eval({expr}) *nvim_eval()* - Evaluates a VimL |expression|. Dictionaries and Lists are - recursively expanded. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {expr} VimL expression string - - Return: ~ - Evaluation result or expanded object - nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()* Evaluates statusline string. @@ -852,29 +782,6 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()* character that uses the highlight. • group: (string) Name of highlight group. -nvim_exec({src}, {output}) *nvim_exec()* - Executes Vimscript (multiline block of Ex-commands), like - anonymous |:source|. - - Unlike |nvim_command()| this function supports heredocs, - script-scope (s:), etc. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {src} Vimscript code - {output} Capture and return all (non-error, non-shell - |:!|) output - - Return: ~ - Output (non-error, non-shell |:!|) if `output` is true, - else empty string. - - See also: ~ - |execute()| - |nvim_command()| - nvim_exec_lua({code}, {args}) *nvim_exec_lua()* Execute Lua code. Parameters (if any) are available as `...` inside the chunk. The chunk can return a value. @@ -1110,14 +1017,8 @@ nvim_get_mode() *nvim_get_mode()* Attributes: ~ {fast} -nvim_get_namespaces() *nvim_get_namespaces()* - Gets existing, non-anonymous namespaces. - - Return: ~ - dict that maps from names to namespace ids. - nvim_get_option({name}) *nvim_get_option()* - Gets an option value string. + Gets the global value of an option. Parameters: ~ {name} Option name @@ -1148,6 +1049,24 @@ nvim_get_option_info({name}) *nvim_get_option_info()* Return: ~ Option Information +nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()* + Gets the value of an option. The behavior of this function + matches that of |:set|: the local value of an option is + returned if it exists; otherwise, the global value is + returned. Local values always correspond to the current buffer + or window. To get a buffer-local or window-local option for a + specific buffer or window, use |nvim_buf_get_option()| or + |nvim_win_get_option()|. + + Parameters: ~ + {name} Option name + {opts} Optional parameters + • scope: One of 'global' or 'local'. Analagous to + |:setglobal| and |:setlocal|, respectively. + + Return: ~ + Option value + nvim_get_proc({pid}) *nvim_get_proc()* Gets info describing process `pid` . @@ -1344,7 +1263,15 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* Parameters: ~ {buffer} the buffer to use (expected to be empty) - {opts} Optional parameters. Reserved for future use. + {opts} Optional parameters. + • on_input: lua callback for input sent, i e + keypresses in terminal mode. Note: keypresses + are sent raw as they would be to the pty + master end. For instance, a carriage return is + sent as a "\r", not as a "\n". |textlock| + applies. It is possible to call + |nvim_chan_send| directly in the callback + however. ["input", term, bufnr, data] Return: ~ Channel id, or 0 on error @@ -1357,104 +1284,6 @@ nvim_out_write({str}) *nvim_out_write()* Parameters: ~ {str} Message - *nvim_parse_expression()* -nvim_parse_expression({expr}, {flags}, {highlight}) - Parse a VimL expression. - - Attributes: ~ - {fast} - - Parameters: ~ - {expr} Expression to parse. Always treated as a - single line. - {flags} Flags: - • "m" if multiple expressions in a row are - allowed (only the first one will be - parsed), - • "E" if EOC tokens are not allowed - (determines whether they will stop parsing - process or be recognized as an - operator/space, though also yielding an - error). - • "l" when needing to start parsing with - lvalues for ":let" or ":for". Common flag - sets: - • "m" to parse like for ":echo". - • "E" to parse like for "<C-r>=". - • empty string for ":call". - • "lm" to parse for ":let". - {highlight} If true, return value will also include - "highlight" key containing array of 4-tuples - (arrays) (Integer, Integer, Integer, String), - where first three numbers define the - highlighted region and represent line, - starting column and ending column (latter - exclusive: one should highlight region - [start_col, end_col)). - - Return: ~ - - • AST: top-level dictionary with these keys: - • "error": Dictionary with error, present only if parser - saw some error. Contains the following keys: - • "message": String, error message in printf format, - translated. Must contain exactly one "%.*s". - • "arg": String, error message argument. - - • "len": Amount of bytes successfully parsed. With flags - equal to "" that should be equal to the length of expr - string. (“Successfully parsed” here means - “participated in AST creation”, not “till the first - error”.) - • "ast": AST, either nil or a dictionary with these - keys: - • "type": node type, one of the value names from - ExprASTNodeType stringified without "kExprNode" - prefix. - • "start": a pair [line, column] describing where node - is "started" where "line" is always 0 (will not be 0 - if you will be using nvim_parse_viml() on e.g. - ":let", but that is not present yet). Both elements - are Integers. - • "len": “length” of the node. This and "start" are - there for debugging purposes primary (debugging - parser and providing debug information). - • "children": a list of nodes described in top/"ast". - There always is zero, one or two children, key will - not be present if node has no children. Maximum - number of children may be found in node_maxchildren - array. - - • Local values (present only for certain nodes): - • "scope": a single Integer, specifies scope for - "Option" and "PlainIdentifier" nodes. For "Option" it - is one of ExprOptScope values, for "PlainIdentifier" - it is one of ExprVarScope values. - • "ident": identifier (without scope, if any), present - for "Option", "PlainIdentifier", "PlainKey" and - "Environment" nodes. - • "name": Integer, register name (one character) or -1. - Only present for "Register" nodes. - • "cmp_type": String, comparison type, one of the value - names from ExprComparisonType, stringified without - "kExprCmp" prefix. Only present for "Comparison" - nodes. - • "ccs_strategy": String, case comparison strategy, one - of the value names from ExprCaseCompareStrategy, - stringified without "kCCStrategy" prefix. Only present - for "Comparison" nodes. - • "augmentation": String, augmentation type for - "Assignment" nodes. Is either an empty string, "Add", - "Subtract" or "Concat" for "=", "+=", "-=" or ".=" - respectively. - • "invert": Boolean, true if result of comparison needs - to be inverted. Only present for "Comparison" nodes. - • "ivalue": Integer, integer value for "Integer" nodes. - • "fvalue": Float, floating-point value for "Float" - nodes. - • "svalue": String, value for "SingleQuotedString" and - "DoubleQuotedString" nodes. - nvim_paste({data}, {crlf}, {phase}) *nvim_paste()* Pastes at cursor, in any mode. @@ -1650,53 +1479,6 @@ nvim_set_current_win({window}) *nvim_set_current_win()* Parameters: ~ {window} Window handle - *nvim_set_decoration_provider()* -nvim_set_decoration_provider({ns_id}, {opts}) - Set or change decoration provider for a namespace - - This is a very general purpose interface for having lua - callbacks being triggered during the redraw code. - - The expected usage is to set extmarks for the currently - redrawn buffer. |nvim_buf_set_extmark| can be called to add - marks on a per-window or per-lines basis. Use the `ephemeral` - key to only use the mark for the current screen redraw (the - callback will be called again for the next redraw ). - - Note: this function should not be called often. Rather, the - callbacks themselves can be used to throttle unneeded - callbacks. the `on_start` callback can return `false` to - disable the provider until the next redraw. Similarly, return - `false` in `on_win` will skip the `on_lines` calls for that - window (but any extmarks set in `on_win` will still be used). - A plugin managing multiple sources of decoration should - ideally only set one provider, and merge the sources - internally. You can use multiple `ns_id` for the extmarks - set/modified inside the callback anyway. - - Note: doing anything other than setting extmarks is considered - experimental. Doing things like changing options are not - expliticly forbidden, but is likely to have unexpected - consequences (such as 100% CPU consumption). doing - `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite - dubious for the moment. - - Parameters: ~ - {ns_id} Namespace id from |nvim_create_namespace()| - {opts} Callbacks invoked during redraw: - • on_start: called first on each screen redraw - ["start", tick] - • on_buf: called for each buffer being redrawn - (before window callbacks) ["buf", bufnr, tick] - • on_win: called when starting to redraw a - specific window. ["win", winid, bufnr, topline, - botline_guess] - • on_line: called for each buffer line being - redrawn. (The interation with fold lines is - subject to change) ["win", winid, bufnr, row] - • on_end: called at the end of a redraw cycle - ["end", tick] - nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()* Set a highlight group. @@ -1746,11 +1528,25 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()* key is an error. nvim_set_option({name}, {value}) *nvim_set_option()* - Sets an option value. + Sets the global value of an option. + + Parameters: ~ + {name} Option name + {value} New option value + + *nvim_set_option_value()* +nvim_set_option_value({name}, {value}, {*opts}) + Sets the value of an option. The behavior of this function + matches that of |:set|: for global-local options, both the + global and local value are set unless otherwise specified with + {scope}. Parameters: ~ {name} Option name {value} New option value + {opts} Optional parameters + • scope: One of 'global' or 'local'. Analagous to + |:setglobal| and |:setlocal|, respectively. nvim_set_var({name}, {value}) *nvim_set_var()* Sets a global (g:) variable. @@ -1790,6 +1586,185 @@ nvim_unsubscribe({event}) *nvim_unsubscribe()* ============================================================================== +Vimscript Functions *api-vimscript* + + *nvim_call_dict_function()* +nvim_call_dict_function({dict}, {fn}, {args}) + Calls a VimL |Dictionary-function| with the given arguments. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {dict} Dictionary, or String evaluating to a VimL |self| + dict + {fn} Name of the function defined on the VimL dict + {args} Function arguments packed in an Array + + Return: ~ + Result of the function call + +nvim_call_function({fn}, {args}) *nvim_call_function()* + Calls a VimL function with the given arguments. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {fn} Function to call + {args} Function arguments packed in an Array + + Return: ~ + Result of the function call + +nvim_command({command}) *nvim_command()* + Executes an ex-command. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {command} Ex-command string + + See also: ~ + |nvim_exec()| + +nvim_eval({expr}) *nvim_eval()* + Evaluates a VimL |expression|. Dictionaries and Lists are + recursively expanded. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {expr} VimL expression string + + Return: ~ + Evaluation result or expanded object + +nvim_exec({src}, {output}) *nvim_exec()* + Executes Vimscript (multiline block of Ex-commands), like + anonymous |:source|. + + Unlike |nvim_command()| this function supports heredocs, + script-scope (s:), etc. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {src} Vimscript code + {output} Capture and return all (non-error, non-shell + |:!|) output + + Return: ~ + Output (non-error, non-shell |:!|) if `output` is true, + else empty string. + + See also: ~ + |execute()| + |nvim_command()| + + *nvim_parse_expression()* +nvim_parse_expression({expr}, {flags}, {highlight}) + Parse a VimL expression. + + Attributes: ~ + {fast} + + Parameters: ~ + {expr} Expression to parse. Always treated as a + single line. + {flags} Flags: + • "m" if multiple expressions in a row are + allowed (only the first one will be + parsed), + • "E" if EOC tokens are not allowed + (determines whether they will stop parsing + process or be recognized as an + operator/space, though also yielding an + error). + • "l" when needing to start parsing with + lvalues for ":let" or ":for". Common flag + sets: + • "m" to parse like for ":echo". + • "E" to parse like for "<C-r>=". + • empty string for ":call". + • "lm" to parse for ":let". + {highlight} If true, return value will also include + "highlight" key containing array of 4-tuples + (arrays) (Integer, Integer, Integer, String), + where first three numbers define the + highlighted region and represent line, + starting column and ending column (latter + exclusive: one should highlight region + [start_col, end_col)). + + Return: ~ + + • AST: top-level dictionary with these keys: + • "error": Dictionary with error, present only if parser + saw some error. Contains the following keys: + • "message": String, error message in printf format, + translated. Must contain exactly one "%.*s". + • "arg": String, error message argument. + + • "len": Amount of bytes successfully parsed. With flags + equal to "" that should be equal to the length of expr + string. (“Successfully parsed” here means + “participated in AST creation”, not “till the first + error”.) + • "ast": AST, either nil or a dictionary with these + keys: + • "type": node type, one of the value names from + ExprASTNodeType stringified without "kExprNode" + prefix. + • "start": a pair [line, column] describing where node + is "started" where "line" is always 0 (will not be 0 + if you will be using nvim_parse_viml() on e.g. + ":let", but that is not present yet). Both elements + are Integers. + • "len": “length” of the node. This and "start" are + there for debugging purposes primary (debugging + parser and providing debug information). + • "children": a list of nodes described in top/"ast". + There always is zero, one or two children, key will + not be present if node has no children. Maximum + number of children may be found in node_maxchildren + array. + + • Local values (present only for certain nodes): + • "scope": a single Integer, specifies scope for + "Option" and "PlainIdentifier" nodes. For "Option" it + is one of ExprOptScope values, for "PlainIdentifier" + it is one of ExprVarScope values. + • "ident": identifier (without scope, if any), present + for "Option", "PlainIdentifier", "PlainKey" and + "Environment" nodes. + • "name": Integer, register name (one character) or -1. + Only present for "Register" nodes. + • "cmp_type": String, comparison type, one of the value + names from ExprComparisonType, stringified without + "kExprCmp" prefix. Only present for "Comparison" + nodes. + • "ccs_strategy": String, case comparison strategy, one + of the value names from ExprCaseCompareStrategy, + stringified without "kCCStrategy" prefix. Only present + for "Comparison" nodes. + • "augmentation": String, augmentation type for + "Assignment" nodes. Is either an empty string, "Add", + "Subtract" or "Concat" for "=", "+=", "-=" or ".=" + respectively. + • "invert": Boolean, true if result of comparison needs + to be inverted. Only present for "Comparison" nodes. + • "ivalue": Integer, integer value for "Integer" nodes. + • "fvalue": Float, floating-point value for "Float" + nodes. + • "svalue": String, value for "SingleQuotedString" and + "DoubleQuotedString" nodes. + + +============================================================================== Buffer Functions *api-buffer* @@ -1815,48 +1790,6 @@ nvim__buf_redraw_range({buffer}, {first}, {last}) nvim__buf_stats({buffer}) *nvim__buf_stats()* TODO: Documentation - *nvim_buf_add_highlight()* -nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start}, - {col_end}) - Adds a highlight to buffer. - - Useful for plugins that dynamically generate highlights to a - buffer (like a semantic highlighter or linter). The function - adds a single highlight to a buffer. Unlike |matchaddpos()| - highlights follow changes to line numbering (as lines are - inserted/removed above the highlighted line), like signs and - marks do. - - Namespaces are used for batch deletion/updating of a set of - highlights. To create a namespace, use - |nvim_create_namespace()| which returns a namespace id. Pass - it in to this function as `ns_id` to add highlights to the - namespace. All highlights in the same namespace can then be - cleared with single call to |nvim_buf_clear_namespace()|. If - the highlight never will be deleted by an API call, pass - `ns_id = -1` . - - As a shorthand, `ns_id = 0` can be used to create a new - namespace for the highlight, the allocated id is then - returned. If `hl_group` is the empty string no highlight is - added, but a new `ns_id` is still returned. This is supported - for backwards compatibility, new code should use - |nvim_create_namespace()| to create a new empty namespace. - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} namespace to use or -1 for ungrouped - highlight - {hl_group} Name of the highlight group to use - {line} Line to highlight (zero-indexed) - {col_start} Start of (byte-indexed) column range to - highlight - {col_end} End of (byte-indexed) column range to - highlight, or -1 to highlight to end of line - - Return: ~ - The ns_id that was used - nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* Activates buffer-update events on a channel, or as Lua callbacks. @@ -1950,7 +1883,7 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()* switched If a window inside the current tabpage (including a float) already shows the buffer One of these windows will be set as current window temporarily. Otherwise a temporary - scratch window (calleed the "autocmd window" for historical + scratch window (called the "autocmd window" for historical reasons) will be used. This is useful e.g. to call vimL functions that only work with @@ -1965,33 +1898,6 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()* Return value of function. NB: will deepcopy lua values currently, use upvalues to send lua references in and out. - *nvim_buf_clear_namespace()* -nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) - Clears namespaced objects (highlights, extmarks, virtual text) - from a region. - - Lines are 0-indexed. |api-indexing| To clear the namespace in - the entire buffer, specify line_start=0 and line_end=-1. - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace to clear, or -1 to clear all - namespaces. - {line_start} Start of range of lines to clear - {line_end} End of range of lines to clear (exclusive) - or -1 to clear to end of buffer. - -nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* - Removes an extmark. - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace id from |nvim_create_namespace()| - {id} Extmark id - - Return: ~ - true if the extmark was found, else false - nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()* Unmaps a buffer-local |mapping| for the given mode. @@ -2073,73 +1979,6 @@ nvim_buf_get_commands({buffer}, {*opts}) *nvim_buf_get_commands()* Return: ~ Map of maps describing commands. - *nvim_buf_get_extmark_by_id()* -nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts}) - Gets the position (0-indexed) of an extmark. - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace id from |nvim_create_namespace()| - {id} Extmark id - {opts} Optional parameters. Keys: - • details: Whether to include the details dict - - Return: ~ - 0-indexed (row, col) tuple or empty list () if extmark id - was absent - - *nvim_buf_get_extmarks()* -nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) - Gets extmarks in "traversal order" from a |charwise| region - defined by buffer positions (inclusive, 0-indexed - |api-indexing|). - - Region can be given as (row,col) tuples, or valid extmark ids - (whose positions define the bounds). 0 and -1 are understood - as (0,0) and (-1,-1) respectively, thus the following are - equivalent: -> - nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) - nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) -< - - If `end` is less than `start` , traversal works backwards. - (Useful with `limit` , to get the first marks prior to a given - position.) - - Example: -> - local a = vim.api - local pos = a.nvim_win_get_cursor(0) - local ns = a.nvim_create_namespace('my-plugin') - -- Create new extmark at line 1, column 1. - local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) - -- Create new extmark at line 3, column 1. - local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) - -- Get extmarks only from line 3. - local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) - -- Get all marks in this buffer + namespace. - local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {}) - print(vim.inspect(ms)) -< - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace id from |nvim_create_namespace()| - {start} Start of range: a 0-indexed (row, col) or valid - extmark id (whose position defines the bound). - |api-indexing| - {end} End of range (inclusive): a 0-indexed (row, col) - or valid extmark id (whose position defines the - bound). |api-indexing| - {opts} Optional parameters. Keys: - • limit: Maximum number of marks to return - • details Whether to include the details dict - - Return: ~ - List of [extmark_id, row, col] tuples in "traversal - order". - nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()* Gets a list of buffer-local |mapping| definitions. @@ -2270,6 +2109,257 @@ nvim_buf_line_count({buffer}) *nvim_buf_line_count()* Return: ~ Line count, or 0 for unloaded buffer. |api-buffer| + *nvim_buf_set_keymap()* +nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {*opts}) + Sets a buffer-local |mapping| for the given mode. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + + See also: ~ + |nvim_set_keymap()| + + *nvim_buf_set_lines()* +nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement}) + Sets (replaces) a line-range in the buffer. + + Indexing is zero-based, end-exclusive. Negative indices are + interpreted as length+1+index: -1 refers to the index past the + end. So to change or delete the last element use start=-2 and + end=-1. + + To insert lines at a given index, set `start` and `end` to the + same index. To delete a range of lines, set `replacement` to + an empty array. + + Out-of-bounds indices are clamped to the nearest valid value, + unless `strict_indexing` is set. + + Attributes: ~ + not allowed when |textlock| is active + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {start} First line index + {end} Last line index (exclusive) + {strict_indexing} Whether out-of-bounds should be an + error. + {replacement} Array of lines to use as replacement + + *nvim_buf_set_mark()* +nvim_buf_set_mark({buffer}, {name}, {line}, {col}, {opts}) + Sets a named mark in the given buffer, all marks are allowed + file/uppercase, visual, last change, etc. See |mark-motions|. + + Marks are (1,0)-indexed. |api-indexing| + + Note: + Passing 0 as line deletes the mark + + Parameters: ~ + {buffer} Buffer to set the mark on + {name} Mark name + {line} Line number + {col} Column/row number + {opts} Optional parameters. Reserved for future use. + + Return: ~ + true if the mark was set, else false. + + See also: ~ + |nvim_buf_del_mark()| + |nvim_buf_get_mark()| + +nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()* + Sets the full file name for a buffer + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Buffer name + +nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* + Sets a buffer option value. Passing 'nil' as value deletes the + option (only works if there's a global fallback) + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Option name + {value} Option value + + *nvim_buf_set_text()* +nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col}, + {replacement}) + Sets (replaces) a range in the buffer + + This is recommended over nvim_buf_set_lines when only + modifying parts of a line, as extmarks will be preserved on + non-modified parts of the touched lines. + + Indexing is zero-based and end-exclusive. + + To insert text at a given index, set `start` and `end` ranges + to the same index. To delete a range, set `replacement` to an + array containing an empty string, or simply an empty array. + + Prefer nvim_buf_set_lines when adding or deleting entire lines + only. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {start_row} First line index + {start_column} First column + {end_row} Last line index + {end_column} Last column + {replacement} Array of lines to use as replacement + +nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* + Sets a buffer-scoped (b:) variable + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Variable name + {value} Variable value + + +============================================================================== +Extmark Functions *api-extmark* + + *nvim_buf_add_highlight()* +nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start}, + {col_end}) + Adds a highlight to buffer. + + Useful for plugins that dynamically generate highlights to a + buffer (like a semantic highlighter or linter). The function + adds a single highlight to a buffer. Unlike |matchaddpos()| + highlights follow changes to line numbering (as lines are + inserted/removed above the highlighted line), like signs and + marks do. + + Namespaces are used for batch deletion/updating of a set of + highlights. To create a namespace, use + |nvim_create_namespace()| which returns a namespace id. Pass + it in to this function as `ns_id` to add highlights to the + namespace. All highlights in the same namespace can then be + cleared with single call to |nvim_buf_clear_namespace()|. If + the highlight never will be deleted by an API call, pass + `ns_id = -1` . + + As a shorthand, `ns_id = 0` can be used to create a new + namespace for the highlight, the allocated id is then + returned. If `hl_group` is the empty string no highlight is + added, but a new `ns_id` is still returned. This is supported + for backwards compatibility, new code should use + |nvim_create_namespace()| to create a new empty namespace. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {ns_id} namespace to use or -1 for ungrouped + highlight + {hl_group} Name of the highlight group to use + {line} Line to highlight (zero-indexed) + {col_start} Start of (byte-indexed) column range to + highlight + {col_end} End of (byte-indexed) column range to + highlight, or -1 to highlight to end of line + + Return: ~ + The ns_id that was used + + *nvim_buf_clear_namespace()* +nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) + Clears namespaced objects (highlights, extmarks, virtual text) + from a region. + + Lines are 0-indexed. |api-indexing| To clear the namespace in + the entire buffer, specify line_start=0 and line_end=-1. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace to clear, or -1 to clear all + namespaces. + {line_start} Start of range of lines to clear + {line_end} End of range of lines to clear (exclusive) + or -1 to clear to end of buffer. + +nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* + Removes an extmark. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id + + Return: ~ + true if the extmark was found, else false + + *nvim_buf_get_extmark_by_id()* +nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts}) + Gets the position (0-indexed) of an extmark. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id + {opts} Optional parameters. Keys: + • details: Whether to include the details dict + + Return: ~ + 0-indexed (row, col) tuple or empty list () if extmark id + was absent + + *nvim_buf_get_extmarks()* +nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) + Gets extmarks in "traversal order" from a |charwise| region + defined by buffer positions (inclusive, 0-indexed + |api-indexing|). + + Region can be given as (row,col) tuples, or valid extmark ids + (whose positions define the bounds). 0 and -1 are understood + as (0,0) and (-1,-1) respectively, thus the following are + equivalent: +> + nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) + nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) +< + + If `end` is less than `start` , traversal works backwards. + (Useful with `limit` , to get the first marks prior to a given + position.) + + Example: +> + local a = vim.api + local pos = a.nvim_win_get_cursor(0) + local ns = a.nvim_create_namespace('my-plugin') + -- Create new extmark at line 1, column 1. + local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) + -- Create new extmark at line 3, column 1. + local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) + -- Get extmarks only from line 3. + local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) + -- Get all marks in this buffer + namespace. + local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {}) + print(vim.inspect(ms)) +< + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {start} Start of range: a 0-indexed (row, col) or valid + extmark id (whose position defines the bound). + |api-indexing| + {end} End of range (inclusive): a 0-indexed (row, col) + or valid extmark id (whose position defines the + bound). |api-indexing| + {opts} Optional parameters. Keys: + • limit: Maximum number of marks to return + • details Whether to include the details dict + + Return: ~ + List of [extmark_id, row, col] tuples in "traversal + order". + *nvim_buf_set_extmark()* nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) Creates or updates an extmark. @@ -2295,7 +2385,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) |api-indexing| {opts} Optional parameters. • id : id of the extmark to edit. - • end_line : ending line of the mark, 0-based + • end_row : ending line of the mark, 0-based inclusive. • end_col : ending col of the mark, 0-based exclusive. @@ -2351,12 +2441,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) of the array. However the 'tabstop' buffer option is still used for hard tabs. By default lines are placed below the buffer line - containing the mark. • Note: currently virtual lines are limited to - one block per buffer. Thus setting a new mark - disables any previous `virt_lines` decoration. - However plugins should not rely on this - behaviour, as this limitation is planned to be - removed. + containing the mark. • virt_lines_above: place virtual lines above instead. • virt_lines_leftcol: Place extmarks in the @@ -2383,116 +2468,74 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) Return: ~ Id of the created/updated extmark - *nvim_buf_set_keymap()* -nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {*opts}) - Sets a buffer-local |mapping| for the given mode. - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - - See also: ~ - |nvim_set_keymap()| - - *nvim_buf_set_lines()* -nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement}) - Sets (replaces) a line-range in the buffer. - - Indexing is zero-based, end-exclusive. Negative indices are - interpreted as length+1+index: -1 refers to the index past the - end. So to change or delete the last element use start=-2 and - end=-1. - - To insert lines at a given index, set `start` and `end` to the - same index. To delete a range of lines, set `replacement` to - an empty array. - - Out-of-bounds indices are clamped to the nearest valid value, - unless `strict_indexing` is set. - - Attributes: ~ - not allowed when |textlock| is active - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {start} First line index - {end} Last line index (exclusive) - {strict_indexing} Whether out-of-bounds should be an - error. - {replacement} Array of lines to use as replacement - - *nvim_buf_set_mark()* -nvim_buf_set_mark({buffer}, {name}, {line}, {col}, {opts}) - Sets a named mark in the given buffer, all marks are allowed - file/uppercase, visual, last change, etc. See |mark-motions|. +nvim_create_namespace({name}) *nvim_create_namespace()* + Creates a new *namespace* or gets an existing one. - Marks are (1,0)-indexed. |api-indexing| + Namespaces are used for buffer highlights and virtual text, + see |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. - Note: - Passing 0 as line deletes the mark + Namespaces can be named or anonymous. If `name` matches an + existing namespace, the associated id is returned. If `name` + is an empty string a new, anonymous namespace is created. Parameters: ~ - {buffer} Buffer to set the mark on - {name} Mark name - {line} Line number - {col} Column/row number - {opts} Optional parameters. Reserved for future use. + {name} Namespace name or empty string Return: ~ - true if the mark was set, else false. - - See also: ~ - |nvim_buf_del_mark()| - |nvim_buf_get_mark()| - -nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()* - Sets the full file name for a buffer - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {name} Buffer name - -nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* - Sets a buffer option value. Passing 'nil' as value deletes the - option (only works if there's a global fallback) - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {name} Option name - {value} Option value + Namespace id - *nvim_buf_set_text()* -nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col}, - {replacement}) - Sets (replaces) a range in the buffer +nvim_get_namespaces() *nvim_get_namespaces()* + Gets existing, non-anonymous namespaces. - This is recommended over nvim_buf_set_lines when only - modifying parts of a line, as extmarks will be preserved on - non-modified parts of the touched lines. + Return: ~ + dict that maps from names to namespace ids. - Indexing is zero-based and end-exclusive. + *nvim_set_decoration_provider()* +nvim_set_decoration_provider({ns_id}, {opts}) + Set or change decoration provider for a namespace - To insert text at a given index, set `start` and `end` ranges - to the same index. To delete a range, set `replacement` to an - array containing an empty string, or simply an empty array. + This is a very general purpose interface for having lua + callbacks being triggered during the redraw code. - Prefer nvim_buf_set_lines when adding or deleting entire lines - only. + The expected usage is to set extmarks for the currently + redrawn buffer. |nvim_buf_set_extmark| can be called to add + marks on a per-window or per-lines basis. Use the `ephemeral` + key to only use the mark for the current screen redraw (the + callback will be called again for the next redraw ). - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {start_row} First line index - {start_column} Last column - {end_row} Last line index - {end_column} Last column - {replacement} Array of lines to use as replacement + Note: this function should not be called often. Rather, the + callbacks themselves can be used to throttle unneeded + callbacks. the `on_start` callback can return `false` to + disable the provider until the next redraw. Similarly, return + `false` in `on_win` will skip the `on_lines` calls for that + window (but any extmarks set in `on_win` will still be used). + A plugin managing multiple sources of decoration should + ideally only set one provider, and merge the sources + internally. You can use multiple `ns_id` for the extmarks + set/modified inside the callback anyway. -nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* - Sets a buffer-scoped (b:) variable + Note: doing anything other than setting extmarks is considered + experimental. Doing things like changing options are not + expliticly forbidden, but is likely to have unexpected + consequences (such as 100% CPU consumption). doing + `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite + dubious for the moment. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {name} Variable name - {value} Variable value + {ns_id} Namespace id from |nvim_create_namespace()| + {opts} Callbacks invoked during redraw: + • on_start: called first on each screen redraw + ["start", tick] + • on_buf: called for each buffer being redrawn + (before window callbacks) ["buf", bufnr, tick] + • on_win: called when starting to redraw a + specific window. ["win", winid, bufnr, topline, + botline_guess] + • on_line: called for each buffer line being + redrawn. (The interaction with fold lines is + subject to change) ["win", winid, bufnr, row] + • on_end: called at the end of a redraw cycle + ["end", tick] ============================================================================== diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 6c41dd3b10..3df8d5ced4 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -57,7 +57,7 @@ The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand. See |autocmd-buflocal|. Note: The ":autocmd" command can only be followed by another command when the -'|' appears before {cmd}. This works: > +'|' appears where the pattern is expected. This works: > :augroup mine | au! BufRead | augroup END But this sees "augroup" as part of the defined command: > :augroup mine | au! BufRead * | augroup END @@ -718,7 +718,27 @@ MenuPopup Just before showing the popup menu (under the o Operator-pending i Insert c Command line - *OptionSet* + *ModeChanged* +ModeChanged After changing the mode. The pattern is + matched against `'old_mode:new_mode'`, for + example match against `*:c` to simulate + |CmdlineEnter|. + The following values of |v:event| are set: + old_mode The mode before it changed. + new_mode The new mode as also returned + by |mode()| called with a + non-zero argument. + When ModeChanged is triggered, old_mode will + have the value of new_mode when the event was + last triggered. + This will be triggered on every minor mode + change. + Usage example to use relative line numbers + when entering visual mode: > + :au ModeChanged [vV\x16]*:* let &l:rnu = mode() =~# '^[vV\x16]' + :au ModeChanged *:[vV\x16]* let &l:rnu = mode() =~# '^[vV\x16]' + :au WinEnter,WinLeave * let &l:rnu = mode() =~# '^[vV\x16]' +< *OptionSet* OptionSet After setting an option (except during |startup|). The |autocmd-pattern| is matched against the long option name. |<amatch>| @@ -793,7 +813,7 @@ QuitPre When using `:quit`, `:wq` or `:qall`, before before QuitPre is triggered. Can be used to close any non-essential window if the current window is the last ordinary window. - See also |ExitPre|, ||WinClosed|. + See also |ExitPre|, |WinClosed|. *RemoteReply* RemoteReply When a reply from a Vim that functions as server was received |server2client()|. The @@ -804,6 +824,22 @@ RemoteReply When a reply from a Vim that functions as Note that even if an autocommand is defined, the reply should be read with |remote_read()| to consume it. + *SearchWrapped* +SearchWrapped After making a search with |n| or |N| if the + search wraps around the document back to + the start/finish respectively. + *RecordingEnter* +RecordingEnter When a macro starts recording. + The pattern is the current file name, and + |reg_recording()| is the current register that + is used. + *RecordingLeave* +RecordingLeave When a macro stops recording. + The pattern is the current file name, and + |reg_recording()| is the recorded + register. + |reg_recorded()| is only updated after this + event. *SessionLoadPost* SessionLoadPost After loading the session file created using the |:mksession| command. @@ -1002,8 +1038,9 @@ VimResume After Nvim resumes from |suspend| state. *VimSuspend* VimSuspend Before Nvim enters |suspend| state. *WinClosed* -WinClosed After closing a window. <afile> expands to the - |window-ID|. +WinClosed After closing a window. The pattern is + matched against the |window-ID|. Both + <amatch> and <afile> are set to the |window-ID|. After WinLeave. Non-recursive (event cannot trigger itself). See also |ExitPre|, |QuitPre|. diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 656bb10c45..5f376a600e 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -210,6 +210,11 @@ effective prompt text for a buffer, with |prompt_getprompt()|. The user can go to Normal mode and navigate through the buffer. This can be useful to see older output or copy text. +The CTRL-W key can be used to start a window command, such as CTRL-W w to +switch to the next window. This also works in Insert mode (use Shift-CTRL-W +to delete a word). When leaving the window Insert mode will be stopped. When +coming back to the prompt window Insert mode will be restored. + Any command that starts Insert mode, such as "a", "i", "A" and "I", will move the cursor to the last line. "A" will move to the end of the line, "I" to the start of the line. diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 6b46ac9cf2..7716af25bd 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -863,9 +863,11 @@ Note: these are typed literally, they are not special keys! *:<amatch>* *<amatch>* <amatch> When executing autocommands, is replaced with the match for which this autocommand was executed. *E497* - It differs from <afile> only when the file name isn't used - to match with (for FileType, Syntax and SpellFileMissing + It differs from <afile> when the file name isn't used to + match with (for FileType, Syntax and SpellFileMissing events). + When the match is with a file name, it is expanded to the + full path. *:<sfile>* *<sfile>* <sfile> When executing a ":source" command, is replaced with the file name of the sourced file. *E498* diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index d53aae510e..a825435179 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -41,6 +41,7 @@ requires a namespace. *diagnostic-structure* A diagnostic is a Lua table with the following keys: + bufnr: Buffer number lnum: The starting line of the diagnostic end_lnum: The final line of the diagnostic col: The starting column of the diagnostic @@ -48,6 +49,7 @@ A diagnostic is a Lua table with the following keys: severity: The severity of the diagnostic |vim.diagnostic.severity| message: The diagnostic text source: The source of the diagnostic + user_data: Arbitrary data plugins or users can add Diagnostics use the same indexing as the rest of the Nvim API (i.e. 0-based rows and columns). |api-indexing| @@ -239,7 +241,7 @@ DiagnosticUnderlineHint *hl-DiagnosticFloatingError* DiagnosticFloatingError Used to color "Error" diagnostic messages in diagnostics float. - See |vim.diagnostic.show_line_diagnostics()| + See |vim.diagnostic.open_float()| *hl-DiagnosticFloatingWarn* DiagnosticFloatingWarn @@ -289,11 +291,11 @@ option in the "signs" table of |vim.diagnostic.config()| or 10 if unset). ============================================================================== EVENTS *diagnostic-events* - *DiagnosticsChanged* -DiagnosticsChanged After diagnostics have changed. + *DiagnosticChanged* +DiagnosticChanged After diagnostics have changed. Example: > - autocmd User DiagnosticsChanged lua vim.diagnostic.setqflist({open = false }) + autocmd DiagnosticChanged * lua vim.diagnostic.setqflist({open = false }) < ============================================================================== ============================================================================== @@ -345,9 +347,12 @@ config({opts}, {namespace}) *vim.diagnostic.config()* • severity: Only show virtual text for diagnostics matching the given severity |diagnostic-severity| - • source: (string) Include the diagnostic - source in virtual text. One of "always" - or "if_many". + • source: (boolean or string) Include the + diagnostic source in virtual text. Use + "if_many" to only show sources if there + is more than one diagnostic source in the + buffer. Otherwise, any truthy value means + to always show the diagnostic source. • format: (function) A function that takes a diagnostic as input and returns a string. The return value is the text used @@ -373,29 +378,8 @@ config({opts}, {namespace}) *vim.diagnostic.config()* Otherwise, all signs use the same priority. - • float: Options for floating windows: - • severity: See |diagnostic-severity|. - • show_header: (boolean, default true) Show - "Diagnostics:" header - • source: (string) Include the diagnostic - source in the message. One of "always" or - "if_many". - • format: (function) A function that takes - a diagnostic as input and returns a - string. The return value is the text used - to display the diagnostic. - • prefix: (function or string) Prefix each - diagnostic in the floating window. If a - function, it must have the signature - (diagnostic, i, total) -> string, where - {i} is the index of the diagnostic being - evaluated and {total} is the total number - of diagnostics displayed in the window. - The returned string is prepended to each - diagnostic in the window. Otherwise, if - {prefix} is a string, it is prepended to - each diagnostic. - + • float: Options for floating windows. See + |vim.diagnostic.open_float()|. • update_in_insert: (default false) Update diagnostics in Insert mode (if false, diagnostics are updated on InsertLeave) @@ -414,8 +398,9 @@ disable({bufnr}, {namespace}) *vim.diagnostic.disable()* Disable diagnostics in the given buffer. Parameters: ~ - {bufnr} number|nil Buffer number. Defaults to the - current buffer. + {bufnr} number|nil Buffer number, or 0 for current + buffer. When omitted, disable diagnostics in + all buffers. {namespace} number|nil Only disable diagnostics for the given namespace. @@ -423,8 +408,9 @@ enable({bufnr}, {namespace}) *vim.diagnostic.enable()* Enable diagnostics in the given buffer. Parameters: ~ - {bufnr} number|nil Buffer number. Defaults to the - current buffer. + {bufnr} number|nil Buffer number, or 0 for current + buffer. When omitted, enable diagnostics in + all buffers. {namespace} number|nil Only enable diagnostics for the given namespace. @@ -458,7 +444,7 @@ get_namespace({namespace}) *vim.diagnostic.get_namespace()* Get namespace metadata. Parameters: ~ - {ns} number Diagnostic namespace + {namespace} number Diagnostic namespace Return: ~ table Namespace metadata @@ -527,6 +513,9 @@ goto_next({opts}) *vim.diagnostic.goto_next()* "true", call |vim.diagnostic.open_float()| after moving. If a table, pass the table as the {opts} parameter to |vim.diagnostic.open_float()|. + Unless overridden, the float will show + diagnostics at the new cursor position (as if + "cursor" were passed to the "scope" option). • win_id: (number, default 0) Window ID goto_prev({opts}) *vim.diagnostic.goto_prev()* @@ -550,8 +539,9 @@ hide({namespace}, {bufnr}) *vim.diagnostic.hide()* {namespace} number|nil Diagnostic namespace. When omitted, hide diagnostics from all namespaces. - {bufnr} number|nil Buffer number. Defaults to the - current buffer. + {bufnr} number|nil Buffer number, or 0 for current + buffer. When omitted, hide diagnostics in all + buffers. *vim.diagnostic.match()* match({str}, {pat}, {groups}, {severity_map}, {defaults}) @@ -589,45 +579,67 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults}) diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}. -open_float({bufnr}, {opts}) *vim.diagnostic.open_float()* +open_float({opts}, {...}) *vim.diagnostic.open_float()* Show diagnostics in a floating window. Parameters: ~ - {bufnr} number|nil Buffer number. Defaults to the current - buffer. - {opts} table|nil Configuration table with the same keys - as |vim.lsp.util.open_floating_preview()| in - addition to the following: - • namespace: (number) Limit diagnostics to the - given namespace - • scope: (string, default "buffer") Show - diagnostics from the whole buffer ("buffer"), - the current cursor line ("line"), or the - current cursor position ("cursor"). - • pos: (number or table) If {scope} is "line" or - "cursor", use this position rather than the - cursor position. If a number, interpreted as a - line number; otherwise, a (row, col) tuple. - • severity_sort: (default false) Sort diagnostics - by severity. Overrides the setting from - |vim.diagnostic.config()|. - • severity: See |diagnostic-severity|. Overrides - the setting from |vim.diagnostic.config()|. - • show_header: (boolean, default true) Show - "Diagnostics:" header. Overrides the setting - from |vim.diagnostic.config()|. - • source: (string) Include the diagnostic source - in the message. One of "always" or "if_many". - Overrides the setting from - |vim.diagnostic.config()|. - • format: (function) A function that takes a - diagnostic as input and returns a string. The - return value is the text used to display the - diagnostic. Overrides the setting from - |vim.diagnostic.config()|. - • prefix: (function or string) Prefix each - diagnostic in the floating window. Overrides - the setting from |vim.diagnostic.config()|. + {opts} table|nil Configuration table with the same keys + as |vim.lsp.util.open_floating_preview()| in + addition to the following: + • bufnr: (number) Buffer number to show + diagnostics from. Defaults to the current + buffer. + • namespace: (number) Limit diagnostics to the + given namespace + • scope: (string, default "line") Show diagnostics + from the whole buffer ("buffer"), the current + cursor line ("line"), or the current cursor + position ("cursor"). Shorthand versions are also + accepted ("c" for "cursor", "l" for "line", "b" + for "buffer"). + • pos: (number or table) If {scope} is "line" or + "cursor", use this position rather than the + cursor position. If a number, interpreted as a + line number; otherwise, a (row, col) tuple. + • severity_sort: (default false) Sort diagnostics + by severity. Overrides the setting from + |vim.diagnostic.config()|. + • severity: See |diagnostic-severity|. Overrides + the setting from |vim.diagnostic.config()|. + • header: (string or table) String to use as the + header for the floating window. If a table, it + is interpreted as a [text, hl_group] tuple. + Overrides the setting from + |vim.diagnostic.config()|. + • source: (boolean or string) Include the + diagnostic source in the message. Use "if_many" + to only show sources if there is more than one + source of diagnostics in the buffer. Otherwise, + any truthy value means to always show the + diagnostic source. Overrides the setting from + |vim.diagnostic.config()|. + • format: (function) A function that takes a + diagnostic as input and returns a string. The + return value is the text used to display the + diagnostic. Overrides the setting from + |vim.diagnostic.config()|. + • prefix: (function, string, or table) Prefix each + diagnostic in the floating window. If a + function, it must have the signature + (diagnostic, i, total) -> (string, string), + where {i} is the index of the diagnostic being + evaluated and {total} is the total number of + diagnostics displayed in the window. The + function should return a string which is + prepended to each diagnostic in the window as + well as an (optional) highlight group which will + be used to highlight the prefix. If {prefix} is + a table, it is interpreted as a [text, hl_group] + tuple as in |nvim_echo()|; otherwise, if + {prefix} is a string, it is prepended to each + diagnostic in the window with no highlight. + Overrides the setting from + |vim.diagnostic.config()|. Return: ~ tuple ({float_bufnr}, {win_id}) @@ -698,15 +710,16 @@ show({namespace}, {bufnr}, {diagnostics}, {opts}) {namespace} number|nil Diagnostic namespace. When omitted, show diagnostics from all namespaces. - {bufnr} number|nil Buffer number. Defaults to the - current buffer. + {bufnr} number|nil Buffer number, or 0 for current + buffer. When omitted, show diagnostics in + all buffers. {diagnostics} table|nil The diagnostics to display. When omitted, use the saved diagnostics for the given namespace and buffer. This can be used to display a list of diagnostics without saving them or to display only a subset of diagnostics. May not be used when - {namespace} is nil. + {namespace} or {bufnr} is nil. {opts} table|nil Display options. See |vim.diagnostic.config()|. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 1b949d749c..aef303195d 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2042,10 +2042,29 @@ v:option_new New value of the option. Valid while executing an |OptionSet| autocommand. *v:option_old* v:option_old Old value of the option. Valid while executing an |OptionSet| - autocommand. + autocommand. Depending on the command used for setting and the + kind of option this is either the local old value or the + global old value. + *v:option_oldlocal* +v:option_oldlocal + Old local value of the option. Valid while executing an + |OptionSet| autocommand. + *v:option_oldglobal* +v:option_oldglobal + Old global value of the option. Valid while executing an + |OptionSet| autocommand. *v:option_type* v:option_type Scope of the set command. Valid while executing an |OptionSet| autocommand. Can be either "global" or "local" + *v:option_command* +v:option_command + Command used to set the option. Valid while executing an + |OptionSet| autocommand. + value option was set via ~ + "setlocal" |:setlocal| or ":let l:xxx" + "setglobal" |:setglobal| or ":let g:xxx" + "set" |:set| or |:let| + "modeline" |modeline| *v:operator* *operator-variable* v:operator The last operator given in Normal mode. This is a single character except for commands starting with <g> or <z>, @@ -2569,6 +2588,7 @@ readdir({dir} [, {expr}]) List file names in {dir} selected by {expr} readfile({fname} [, {type} [, {max}]]) List get list of lines from file {fname} reg_executing() String get the executing register name +reg_recorded() String get the last recorded register name reg_recording() String get the recording register name reltime([{start} [, {end}]]) List get time value reltimefloat({time}) Float turn the time value into a Float @@ -2992,7 +3012,11 @@ bufadd({name}) *bufadd()* number. Otherwise return the buffer number of the newly created buffer. When {name} is an empty string then a new buffer is always created. - The buffer will not have' 'buflisted' set. + The buffer will not have 'buflisted' set and not be loaded + yet. To add some text to the buffer use this: > + let bufnr = bufadd('someName') + call bufload(bufnr) + call setbufline(bufnr, 1, ['some', 'text']) < Can also be used as a |method|: > let bufnr = 'somename'->bufadd() @@ -3081,7 +3105,7 @@ bufname([{buf}]) *bufname()* bufname(3) name of buffer 3 bufname("%") name of current buffer bufname("file2") name of buffer where "file2" matches. - +< *bufnr()* bufnr([{buf} [, {create}]]) The result is the number of a buffer, as it is displayed by @@ -3258,7 +3282,7 @@ char2nr({string} [, {utf8}]) *char2nr()* Can also be used as a |method|: > GetChar()->char2nr() - +< *charidx()* charidx({string}, {idx} [, {countcc}]) Return the character index of the byte at {idx} in {string}. @@ -3281,6 +3305,9 @@ charidx({string}, {idx} [, {countcc}]) echo charidx('áb́ć', 3) returns 1 echo charidx('áb́ć', 6, 1) returns 4 echo charidx('áb́ć', 16) returns -1 +< + Can also be used as a |method|: > + GetName()->charidx(idx) chdir({dir}) *chdir()* Change the current working directory to {dir}. The scope of @@ -3393,8 +3420,8 @@ complete({startcol}, {matches}) *complete()* *E785* < This isn't very useful, but it shows how it works. Note that an empty string is returned to avoid a zero being inserted. - Can also be used as a |method|, the second argument is passed - in: > + Can also be used as a |method|, the base is passed as the + second argument: > GetMatches()->complete(col('.')) complete_add({expr}) *complete_add()* @@ -3531,7 +3558,7 @@ confirm({msg} [, {choices} [, {default} [, {type}]]]) Can also be used as a |method|in: > BuildMessage()->confirm("&Yes\n&No") - +< *copy()* copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't different from using {expr} directly. @@ -3584,7 +3611,7 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()* Can also be used as a |method|: > mylist->count(val) - +< *cscope_connection()* cscope_connection([{num} , {dbpath} [, {prepend}]]) Checks for the existence of a |cscope| connection. If no @@ -3742,7 +3769,7 @@ deletebufline({buf}, {first}[, {last}]) *deletebufline()* Can also be used as a |method|: > GetBuffer()->deletebufline(1) - +< dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()* Adds a watcher to a dictionary. A dictionary watcher is identified by three components: @@ -3937,7 +3964,7 @@ exepath({expr}) *exepath()* Can also be used as a |method|: > GetCommand()->exepath() - +< *exists()* exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined, zero otherwise. @@ -4360,6 +4387,9 @@ flatten({list} [, {maxdepth}]) *flatten()* :echo flatten([1, [2, [3, 4]], 5], 1) < [1, 2, [3, 4], 5] + Can also be used as a |method|: > + mylist->flatten() +< float2nr({expr}) *float2nr()* Convert {expr} to a Number by omitting the part after the decimal point. @@ -4483,7 +4513,7 @@ foldlevel({lnum}) *foldlevel()* Can also be used as a |method|: > GetLnum()->foldlevel() - +< *foldtext()* foldtext() Returns a String, to be displayed for a closed fold. This is the default function used for the 'foldtext' option and should @@ -4697,7 +4727,9 @@ getbufinfo([{dict}]) < To get buffer-local options use: > getbufvar({bufnr}, '&option_name') - +< + Can also be used as a |method|: > + GetBufnr()->getbufinfo() < *getbufline()* getbufline({buf}, {lnum} [, {end}]) @@ -5001,11 +5033,11 @@ getcurpos() Get the position of the cursor. This is like getpos('.'), but getcwd([{winnr}[, {tabnr}]]) *getcwd()* With no arguments, returns the name of the effective |current-directory|. With {winnr} or {tabnr} the working - directory of that scope is returned. + directory of that scope is returned, and 'autochdir' is + ignored. Tabs and windows are identified by their respective numbers, - 0 means current tab or window. Missing argument implies 0. + 0 means current tab or window. Missing tab number implies 0. Thus the following are equivalent: > - getcwd() getcwd(0) getcwd(0, 0) < If {winnr} is -1 it is ignored, only the tab is resolved. @@ -5136,8 +5168,8 @@ getline({lnum} [, {end}]) digit, |line()| is called to translate the String into a Number. To get the line under the cursor: > getline(".") -< When {lnum} is smaller than 1 or bigger than the number of - lines in the buffer, an empty string is returned. +< When {lnum} is a number smaller than 1 or bigger than the + number of lines in the buffer, an empty string is returned. When {end} is given the result is a |List| where each item is a line from the current buffer in the range {lnum} to {end}, @@ -5199,6 +5231,9 @@ getmarklist([{buf}]) *getmarklist()* Refer to |getpos()| for getting information about a specific mark. + Can also be used as a |method|: > + GetBufnr()->getmarklist() + getmatches([{win}]) *getmatches()* Returns a |List| with all matches previously defined for the current window by |matchadd()| and the |:match| commands. @@ -5562,6 +5597,9 @@ getwininfo([{winid}]) *getwininfo()* otherwise wincol leftmost screen column of the window; "col" from |win_screenpos()| + textoff number of columns occupied by any + 'foldcolumn', 'signcolumn' and line + number in front of the text winid |window-ID| winnr window number winrow topmost screen line of the window; @@ -5849,9 +5887,9 @@ histadd({history}, {item}) *histadd()* :let date=input("Enter date: ") < This function is not available in the |sandbox|. - Can also be used as a |method|, the base is used for the + Can also be used as a |method|, the base is passed as the second argument: > - GetPattern()->histadd('search') + GetHistory()->histadd('search') histdel({history} [, {item}]) *histdel()* Clear {history}, i.e. delete all its entries. See |hist-names| @@ -6496,8 +6534,8 @@ libcall({libname}, {funcname}, {argument}) Examples: > :echo libcall("libc.so", "getenv", "HOME") -< Can also be used as a |method|, where the base is passed as - the argument to the called function: > +< Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->libcall("libc.so", "getenv") < *libcallnr()* @@ -6509,8 +6547,8 @@ libcallnr({libname}, {funcname}, {argument}) :call libcallnr("libc.so", "printf", "Hello World!\n") :call libcallnr("libc.so", "sleep", 10) < - Can also be used as a |method|, where the base is passed as - the argument to the called function: > + Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->libcallnr("libc.so", "printf") < line({expr} [, {winid}]) *line()* @@ -7034,7 +7072,7 @@ matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()* Can also be used as a |method|: > GetText()->matchstrpos('word') - +< *max()* max({expr}) Return the maximum value of all items in {expr}. {expr} can be a |List| or a |Dictionary|. For a Dictionary, @@ -7164,7 +7202,7 @@ mode([expr]) Return a string that indicates the current mode. Rvc Virtual Replace mode completion |compl-generic| Rvx Virtual Replace mode |i_CTRL-X| completion c Command-line editing - cv Vim Ex mode |Q| or |gQ| + cv Vim Ex mode |gQ| r Hit-enter prompt rm The -- more -- prompt r? A |:confirm| query of some sort @@ -7461,15 +7499,18 @@ printf({fmt}, {expr1} ...) *printf()* field width. If the converted value has fewer bytes than the field width, it will be padded with spaces on the left (or right, if the left-adjustment flag has - been given) to fill out the field width. + been given) to fill out the field width. For the S + conversion the count is in cells. .precision An optional precision, in the form of a period '.' followed by an optional digit string. If the digit string is omitted, the precision is taken as zero. This gives the minimum number of digits to appear for - d, o, x, and X conversions, or the maximum number of - bytes to be printed from a string for s conversions. + d, o, x, and X conversions, the maximum number of + bytes to be printed from a string for s conversions, + or the maximum number of cells to be printed from a + string for S conversions. For floating point it is the number of digits after the decimal point. @@ -7581,6 +7622,9 @@ prompt_getprompt({buf}) *prompt_getprompt()* If the buffer doesn't exist or isn't a prompt buffer, an empty string is returned. + Can also be used as a |method|: > + GetBuffer()->prompt_getprompt() + prompt_setcallback({buf}, {expr}) *prompt_setcallback()* Set prompt callback for buffer {buf} to {expr}. When {expr} is an empty string the callback is removed. This has only @@ -7782,6 +7826,11 @@ reg_executing() *reg_executing()* Returns an empty string when no register is being executed. See |@|. +reg_recorded() *reg_recorded()* + Returns the single letter name of the last recorded register. + Returns an empty string string when nothing was recorded yet. + See |q| and |Q|. + reg_recording() *reg_recording()* Returns the single letter name of the register being recorded. Returns an empty string string when not recording. See |q|. @@ -8066,6 +8115,9 @@ screenattr({row}, {col}) *screenattr()* arbitrary number that can only be used to compare to the attribute at other positions. + Can also be used as a |method|: > + GetRow()->screenattr(col) + screenchar({row}, {col}) *screenchar()* The result is a Number, which is the character at position [row, col] on the screen. This works for every possible @@ -8076,6 +8128,9 @@ screenchar({row}, {col}) *screenchar()* This is mainly to be used for testing. Returns -1 when row or col is out of range. + Can also be used as a |method|: > + GetRow()->screenchar(col) + screenchars({row}, {col}) *screenchars()* The result is a List of Numbers. The first number is the same as what |screenchar()| returns. Further numbers are @@ -8083,6 +8138,9 @@ screenchars({row}, {col}) *screenchars()* This is mainly to be used for testing. Returns an empty List when row or col is out of range. + Can also be used as a |method|: > + GetRow()->screenchars(col) + screencol() *screencol()* The result is a Number, which is the current screen column of the cursor. The leftmost column has number 1. @@ -8118,6 +8176,9 @@ screenpos({winid}, {lnum}, {col}) *screenpos()* right position and use |screencol()| to get the value with |conceal| taken into account. + Can also be used as a |method|: > + GetWinid()->screenpos(lnum, col) + screenrow() *screenrow()* The result is a Number, which is the current screen row of the cursor. The top line has number one. @@ -8134,6 +8195,9 @@ screenstring({row}, {col}) *screenstring()* This is mainly to be used for testing. Returns an empty String when row or col is out of range. + Can also be used as a |method|: > + GetRow()->screenstring(col) + search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* Search for regexp pattern {pattern}. The search starts at the cursor position (you can use |cursor()| to set it). @@ -8224,6 +8288,8 @@ search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* without the 'e' flag if the cursor is on the "f" of "if". The 'n' flag tells the function not to move the cursor. + Can also be used as a |method|: > + GetPattern()->search() searchcount([{options}]) *searchcount()* Get or update the last search count, like what is displayed @@ -8344,7 +8410,9 @@ searchcount([{options}]) *searchcount()* value. see |cursor()|, |getpos() (default: cursor's position) - + Can also be used as a |method|: > + GetSearchOpts()->searchcount() +< searchdecl({name} [, {global} [, {thisblock}]]) *searchdecl()* Search for the declaration of {name}. @@ -8363,6 +8431,9 @@ searchdecl({name} [, {global} [, {thisblock}]]) *searchdecl()* echo getline('.') endif < + Can also be used as a |method|: > + GetName()->searchdecl() +< *searchpair()* searchpair({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeout}]]]]) @@ -8477,6 +8548,9 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *searchpos()* < In this example "submatch" is 2 when a lowercase letter is found |/\l|, 3 when an uppercase letter is found |/\u|. + Can also be used as a |method|: > + GetPattern()->searchpos() + server2client({clientid}, {string}) *server2client()* Send a reply string to {clientid}. The most recent {clientid} that sent a string can be retrieved with expand("<client>"). @@ -8488,6 +8562,9 @@ server2client({clientid}, {string}) *server2client()* See also |clientserver|. Example: > :echo server2client(expand("<client>"), "HELLO") + +< Can also be used as a |method|: > + GetClientId()->server2client(string) < serverlist() *serverlist()* Returns a list of server addresses, or empty if all servers @@ -8546,6 +8623,7 @@ setbufline({buf}, {lnum}, {text}) *setbufline()* For the use of {buf}, see |bufname()| above. {lnum} is used like with |setline()|. + Use "$" to refer to the last line in buffer {buf}. When {lnum} is just below the last line the {text} will be added below the last line. On success 0 is returned, on failure 1 is returned. @@ -8553,6 +8631,10 @@ setbufline({buf}, {lnum}, {text}) *setbufline()* If {buf} is not a valid buffer or {lnum} is not valid, an error message is given. + Can also be used as a |method|, the base is passed as the + third argument: > + GetText()->setbufline(buf, lnum) + setbufvar({buf}, {varname}, {val}) *setbufvar()* Set option or local variable {varname} in buffer {buf} to {val}. @@ -8567,6 +8649,10 @@ setbufvar({buf}, {varname}, {val}) *setbufvar()* :call setbufvar("todo", "myvar", "foobar") < This function is not available in the |sandbox|. + Can also be used as a |method|, the base is passed as the + third argument: > + GetValue()->setbufvar(buf, varname) + setcharsearch({dict}) *setcharsearch()* Set the current character search information to {dict}, which contains one or more of the following entries: @@ -8587,6 +8673,9 @@ setcharsearch({dict}) *setcharsearch()* :call setcharsearch(prevsearch) < Also see |getcharsearch()|. + Can also be used as a |method|: > + SavedSearch()->setcharsearch() + setcmdpos({pos}) *setcmdpos()* Set the cursor position in the command line to byte position {pos}. The first position is 1. @@ -8602,6 +8691,9 @@ setcmdpos({pos}) *setcmdpos()* Returns FALSE when successful, TRUE when not editing the command line. + Can also be used as a |method|: > + GetPos()->setcmdpos() + setenv({name}, {val}) *setenv()* Set environment variable {name} to {val}. Example: > call setenv('HOME', '/home/myhome') @@ -8609,6 +8701,10 @@ setenv({name}, {val}) *setenv()* < When {val} is |v:null| the environment variable is deleted. See also |expr-env|. + Can also be used as a |method|, the base is passed as the + second argument: > + GetPath()->setenv('PATH') + setfperm({fname}, {mode}) *setfperm()* *chmod* Set the file permissions for {fname} to {mode}. {mode} must be a string with 9 characters. It is of the form @@ -8654,7 +8750,11 @@ setline({lnum}, {text}) *setline()* < Note: The '[ and '] marks are not set. -setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()* + Can also be used as a |method|, the base is passed as the + second argument: > + GetText()->setline(lnum) + +setloclist({nr}, {list} [, {action} [, {what}]]) *setloclist()* Create or replace or add to the location list for window {nr}. {nr} can be the window number or the |window-ID|. When {nr} is zero the current window is used. @@ -8670,6 +8770,10 @@ setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()* only the items listed in {what} are set. Refer to |setqflist()| for the list of supported keys in {what}. + Can also be used as a |method|, the base is passed as the + second argument: > + GetLoclist()->setloclist(winnr) + setmatches({list} [, {win}]) *setmatches()* Restores a list of matches saved by |getmatches() for the current window|. Returns 0 if successful, otherwise -1. All @@ -8678,6 +8782,9 @@ setmatches({list} [, {win}]) *setmatches()* If {win} is specified, use the window with this number or window ID instead of the current window. + Can also be used as a |method|: > + GetMatches()->setmatches() +< *setpos()* setpos({expr}, {list}) Set the position for String {expr}. Possible values: @@ -8727,8 +8834,10 @@ setpos({expr}, {list}) also set the preferred column. Also see the "curswant" key in |winrestview()|. + Can also be used as a |method|: > + GetPosition()->setpos('.') -setqflist({list} [, {action}[, {what}]]) *setqflist()* +setqflist({list} [, {action} [, {what}]]) *setqflist()* Create or replace or add to the quickfix list. If the optional {what} dictionary argument is supplied, then @@ -8837,7 +8946,10 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()* independent of the 'errorformat' setting. Use a command like `:cc 1` to jump to the first position. - + Can also be used as a |method|, the base is passed as the + second argument: > + GetErrorlist()->setqflist() +< *setreg()* setreg({regname}, {value} [, {options}]) Set the register {regname} to {value}. @@ -8894,6 +9006,10 @@ setreg({regname}, {value} [, {options}]) nothing: > :call setreg('a', '', 'al') +< Can also be used as a |method|, the base is passed as the + second argument: > + GetText()->setreg('a') + settabvar({tabnr}, {varname}, {val}) *settabvar()* Set tab-local variable {varname} to {val} in tab page {tabnr}. |t:var| @@ -8902,6 +9018,10 @@ settabvar({tabnr}, {varname}, {val}) *settabvar()* Tabs are numbered starting with one. This function is not available in the |sandbox|. + Can also be used as a |method|, the base is passed as the + third argument: > + GetValue()->settabvar(tab, name) + settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()* Set option or local variable {varname} in window {winnr} to {val}. @@ -8918,6 +9038,10 @@ settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()* :call settabwinvar(3, 2, "myvar", "foobar") < This function is not available in the |sandbox|. + Can also be used as a |method|, the base is passed as the + fourth argument: > + GetValue()->settabwinvar(tab, winnr, name) + settagstack({nr}, {dict} [, {action}]) *settagstack()* Modify the tag stack of the window {nr} using {dict}. {nr} can be the window number or the |window-ID|. @@ -8951,6 +9075,9 @@ settagstack({nr}, {dict} [, {action}]) *settagstack()* call settagstack(1003, stack) unlet stack < + Can also be used as a |method|, the base is passed as the + second argument: > + GetStack()->settagstack(winnr) setwinvar({nr}, {varname}, {val}) *setwinvar()* Like |settabwinvar()| for the current tab page. @@ -8958,10 +9085,17 @@ setwinvar({nr}, {varname}, {val}) *setwinvar()* :call setwinvar(1, "&list", 0) :call setwinvar(2, "myvar", "foobar") +< Can also be used as a |method|, the base is passed as the + third argument: > + GetValue()->setwinvar(winnr, name) + sha256({string}) *sha256()* Returns a String with 64 hex characters, which is the SHA256 checksum of {string}. + Can also be used as a |method|: > + GetText()->sha256() + shellescape({string} [, {special}]) *shellescape()* Escape {string} for use as a shell command argument. @@ -8970,7 +9104,7 @@ shellescape({string} [, {special}]) *shellescape()* Otherwise encloses {string} in single-quotes and replaces all "'" with "'\''". - If {special} is a ||non-zero-arg|: + If {special} is a |non-zero-arg|: - Special items such as "!", "%", "#" and "<cword>" will be preceded by a backslash. The backslash will be removed again by the |:!| command. @@ -8980,7 +9114,7 @@ shellescape({string} [, {special}]) *shellescape()* - The "!" character will be escaped. This is because csh and tcsh use "!" for history replacement even in single-quotes. - The <NL> character is escaped (twice if {special} is - a ||non-zero-arg|). + a |non-zero-arg|). If 'shell' contains "fish" in the tail, the "\" character will be escaped because in fish it is used as an escape character @@ -8993,6 +9127,8 @@ shellescape({string} [, {special}]) *shellescape()* :call system("chmod +w -- " . shellescape(expand("%"))) < See also |::S|. + Can also be used as a |method|: > + GetCommand()->shellescape() shiftwidth([{col}]) *shiftwidth()* Returns the effective value of 'shiftwidth'. This is the @@ -9015,6 +9151,9 @@ shiftwidth([{col}]) *shiftwidth()* 'vartabstop' feature. If no {col} argument is given, column 1 will be assumed. + Can also be used as a |method|: > + GetColumn()->shiftwidth() + sign_ functions are documented here: |sign-functions-details| simplify({filename}) *simplify()* @@ -9034,6 +9173,8 @@ simplify({filename}) *simplify()* directory. In order to resolve all the involved symbolic links before simplifying the path name, use |resolve()|. + Can also be used as a |method|: > + GetName()->simplify() sin({expr}) *sin()* Return the sine of {expr}, measured in radians, as a |Float|. @@ -9165,6 +9306,9 @@ soundfold({word}) This can be used for making spelling suggestions. Note that the method can be quite slow. + Can also be used as a |method|: > + GetWord()->soundfold() +< *spellbadword()* spellbadword([{sentence}]) Without argument: The result is the badly spelled word under @@ -9190,6 +9334,9 @@ spellbadword([{sentence}]) The spelling information for the current window and the value of 'spelllang' are used. + Can also be used as a |method|: > + GetText()->spellbadword() +< *spellsuggest()* spellsuggest({word} [, {max} [, {capital}]]) Return a |List| with spelling suggestions to replace {word}. @@ -9212,6 +9359,8 @@ spellsuggest({word} [, {max} [, {capital}]]) The spelling information for the current window is used. The values of 'spelllang' and 'spellsuggest' are used. + Can also be used as a |method|: > + GetWord()->spellsuggest() split({string} [, {pattern} [, {keepempty}]]) *split()* Make a |List| out of {string}. When {pattern} is omitted or @@ -9335,7 +9484,7 @@ str2nr({string} [, {base}]) *str2nr()* When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as with the default String to Number conversion. Example: > - let nr = str2nr('123') + let nr = str2nr('0123') < When {base} is 16 a leading "0x" or "0X" is ignored. With a different base the result will be zero. Similarly, when @@ -9343,6 +9492,20 @@ str2nr({string} [, {base}]) *str2nr()* {base} is 2 a leading "0b" or "0B" is ignored. Text after the number is silently ignored. + Can also be used as a |method|: > + GetText()->str2nr() + +strcharpart({src}, {start} [, {len}]) *strcharpart()* + Like |strpart()| but using character index and length instead + of byte index and length. Composing characters are counted + separately. + When a character index is used where a character does not + exist it is assumed to be one character. For example: > + strcharpart('abc', -1, 2) +< results in 'a'. + + Can also be used as a |method|: > + GetText()->strcharpart(5) strchars({string} [, {skipcc}]) *strchars()* The result is a Number, which is the number of characters @@ -9368,14 +9531,8 @@ strchars({string} [, {skipcc}]) *strchars()* endfunction endif < -strcharpart({src}, {start} [, {len}]) *strcharpart()* - Like |strpart()| but using character index and length instead - of byte index and length. Composing characters are counted - separately. - When a character index is used where a character does not - exist it is assumed to be one character. For example: > - strcharpart('abc', -1, 2) -< results in 'a'. + Can also be used as a |method|: > + GetText()->strchars() strdisplaywidth({string} [, {col}]) *strdisplaywidth()* The result is a Number, which is the number of display cells @@ -9390,6 +9547,9 @@ strdisplaywidth({string} [, {col}]) *strdisplaywidth()* Ambiguous, this function's return value depends on 'ambiwidth'. Also see |strlen()|, |strwidth()| and |strchars()|. + Can also be used as a |method|: > + GetText()->strdisplaywidth() + strftime({format} [, {time}]) *strftime()* The result is a String, which is a formatted date and time, as specified by the {format} string. The given {time} is used, @@ -9407,12 +9567,18 @@ strftime({format} [, {time}]) *strftime()* :echo strftime("%c", getftime("file.c")) Show mod time of file.c. +< Can also be used as a |method|: > + GetFormat()->strftime() + strgetchar({str}, {index}) *strgetchar()* Get character {index} from {str}. This uses a character index, not a byte index. Composing characters are considered separate characters here. Also see |strcharpart()| and |strchars()|. + Can also be used as a |method|: > + GetText()->strgetchar(5) + stridx({haystack}, {needle} [, {start}]) *stridx()* The result is a Number, which gives the byte index in {haystack} of the first occurrence of the String {needle}. @@ -9432,6 +9598,9 @@ stridx({haystack}, {needle} [, {start}]) *stridx()* stridx() works similar to the C function strstr(). When used with a single character it works similar to strchr(). + Can also be used as a |method|: > + GetHaystack()->stridx(needle) + *string()* string({expr}) Return {expr} converted to a String. If {expr} is a Number, Float, String, Blob or a composition of them, then the result @@ -9494,6 +9663,9 @@ strpart({src}, {start} [, {len} [, {chars}]]) *strpart()* example, to get the character under the cursor: > strpart(getline("."), col(".") - 1, 1, v:true) < + Can also be used as a |method|: > + GetText()->strpart(5) + strptime({format}, {timestring}) *strptime()* The result is a Number, which is a unix timestamp representing the date and time in {timestring}, which is expected to match @@ -9518,7 +9690,9 @@ strptime({format}, {timestring}) *strptime()* :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600) < Sun Apr 27 12:53:55 1997 - + Can also be used as a |method|: > + GetFormat()->strptime(timestring) +< strridx({haystack}, {needle} [, {start}]) *strridx()* The result is a Number, which gives the byte index in {haystack} of the last occurrence of the String {needle}. @@ -9537,6 +9711,9 @@ strridx({haystack}, {needle} [, {start}]) *strridx()* When used with a single character it works similar to the C function strrchr(). + Can also be used as a |method|: > + GetHaystack()->strridx(needle) + strtrans({string}) *strtrans()* The result is a String, which is {string} with all unprintable characters translated into printable characters |'isprint'|. @@ -9585,6 +9762,9 @@ submatch({nr} [, {list}]) *submatch()* *E935* < This finds the first number in the line and adds one to it. A line break is included as a newline character. + Can also be used as a |method|: > + GetNr()->submatch() + substitute({string}, {pat}, {sub}, {flags}) *substitute()* The result is a String, which is a copy of {string}, in which the first match of {pat} is replaced with {sub}. @@ -9647,6 +9827,9 @@ swapinfo({fname}) *swapinfo()* Not a swap file: does not contain correct block ID Magic number mismatch: Info in first block is invalid + Can also be used as a |method|: > + GetFilename()->swapinfo() + swapname({buf}) *swapname()* The result is the swap file path of the buffer {buf}. For the use of {buf}, see |bufname()| above. @@ -9654,6 +9837,9 @@ swapname({buf}) *swapname()* |:swapname| (unless there is no swap file). If buffer {buf} has no swap file, returns an empty string. + Can also be used as a |method|: > + GetBufname()->swapname() + synID({lnum}, {col}, {trans}) *synID()* The result is a Number, which is the syntax ID at the position {lnum} and {col} in the current window. @@ -9850,6 +10036,8 @@ tabpagebuflist([{arg}]) *tabpagebuflist()* endfor < Note that a buffer may appear in more than one window. + Can also be used as a |method|: > + GetTabpage()->tabpagebuflist() tabpagenr([{arg}]) *tabpagenr()* The result is a Number, which is the number of the current @@ -9876,6 +10064,9 @@ tabpagewinnr({tabarg} [, {arg}]) *tabpagewinnr()* tabpagewinnr(4, '$') " number of windows in tab page 4 < When {tabarg} is invalid zero is returned. + Can also be used as a |method|: > + GetTabpage()->tabpagewinnr() +< *tagfiles()* tagfiles() Returns a |List| with the file names used to search for tags for the current buffer. This is the 'tags' option expanded. @@ -9924,6 +10115,9 @@ taglist({expr} [, {filename}]) *taglist()* located by Vim. Refer to |tags-file-format| for the format of the tags file generated by the different ctags tools. + Can also be used as a |method|: > + GetTagpattern()->taglist() + tempname() *tempname()* *temp-file-name* The result is a String, which is the name of a file that doesn't exist. It can be used for a temporary file. Example: > @@ -9991,6 +10185,9 @@ timer_info([{id}]) -1 means forever "callback" the callback + Can also be used as a |method|: > + GetTimer()->timer_info() +< timer_pause({timer}, {paused}) *timer_pause()* Pause or unpause a timer. A paused timer does not invoke its callback when its time expires. Unpausing a timer may cause @@ -10004,6 +10201,9 @@ timer_pause({timer}, {paused}) *timer_pause()* String, then the timer is paused, otherwise it is unpaused. See |non-zero-arg|. + Can also be used as a |method|: > + GetTimer()->timer_pause(1) +< *timer_start()* *timer* *timers* timer_start({time}, {callback} [, {options}]) Create a timer and return the timer ID. @@ -10031,11 +10231,19 @@ timer_start({time}, {callback} [, {options}]) \ {'repeat': 3}) < This invokes MyHandler() three times at 500 msec intervals. + Can also be used as a |method|: > + GetMsec()->timer_start(callback) + +< Not available in the |sandbox|. + timer_stop({timer}) *timer_stop()* Stop a timer. The timer callback will no longer be invoked. {timer} is an ID returned by timer_start(), thus it must be a Number. If {timer} does not exist there is no error. + Can also be used as a |method|: > + GetTimer()->timer_stop() +< timer_stopall() *timer_stopall()* Stop all timers. The timer callbacks will no longer be invoked. Useful if some timers is misbehaving. If there are @@ -10046,11 +10254,17 @@ tolower({expr}) *tolower()* characters turned into lowercase (just like applying |gu| to the string). + Can also be used as a |method|: > + GetText()->tolower() + toupper({expr}) *toupper()* The result is a copy of the String given, with all lowercase characters turned into uppercase (just like applying |gU| to the string). + Can also be used as a |method|: > + GetText()->toupper() + tr({src}, {fromstr}, {tostr}) *tr()* The result is a copy of the {src} string with all characters which appear in {fromstr} replaced by the character in that @@ -10065,6 +10279,9 @@ tr({src}, {fromstr}, {tostr}) *tr()* echo tr("<blob>", "<>", "{}") < returns "{blob}" + Can also be used as a |method|: > + GetText()->tr(from, to) + trim({text} [, {mask} [, {dir}]]) *trim()* Return {text} as a String where any character in {mask} is removed from the beginning and/or end of {text}. @@ -10088,6 +10305,9 @@ trim({text} [, {mask} [, {dir}]]) *trim()* echo trim(" vim ", " ", 2) < returns " vim" + Can also be used as a |method|: > + GetText()->trim() + trunc({expr}) *trunc()* Return the largest integral value with magnitude less than or equal to {expr} as a |Float| (truncate towards zero). @@ -10144,6 +10364,9 @@ undofile({name}) *undofile()* buffer without a file name will not write an undo file. Useful in combination with |:wundo| and |:rundo|. + Can also be used as a |method|: > + GetFilename()->undofile() + undotree() *undotree()* Return the current state of the undo tree in a dictionary with the following items: @@ -10242,8 +10465,10 @@ virtcol({expr}) *virtcol()* all lines: > echo max(map(range(1, line('$')), "virtcol([v:val, '$'])")) +< Can also be used as a |method|: > + GetPos()->virtcol() -visualmode([expr]) *visualmode()* +visualmode([{expr}]) *visualmode()* The result is a String, which describes the last Visual mode used in the current buffer. Initially it returns an empty string, but once Visual mode has been used, it returns "v", @@ -10257,7 +10482,7 @@ visualmode([expr]) *visualmode()* Visual mode that was used. If Visual mode is active, use |mode()| to get the Visual mode (e.g., in a |:vmap|). - If [expr] is supplied and it evaluates to a non-zero Number or + If {expr} is supplied and it evaluates to a non-zero Number or a non-empty String, then the Visual mode will be cleared and the old value is returned. See |non-zero-arg|. @@ -10291,14 +10516,23 @@ wildmenumode() *wildmenumode()* win_execute({id}, {command} [, {silent}]) *win_execute()* Like `execute()` but in the context of window {id}. The window will temporarily be made the current window, - without triggering autocommands. + without triggering autocommands or changing directory. When + executing {command} autocommands will be triggered, this may + have unexpected side effects. Use |:noautocmd| if needed. Example: > call win_execute(winid, 'syntax enable') +< + Can also be used as a |method|, the base is passed as the + second argument: > + GetCommand()->win_execute(winid) win_findbuf({bufnr}) *win_findbuf()* Returns a |List| with |window-ID|s for windows that contain buffer {bufnr}. When there is none the list is empty. + Can also be used as a |method|: > + GetBufnr()->win_findbuf() + win_getid([{win} [, {tab}]]) *win_getid()* Get the |window-ID| for the specified window. When {win} is missing use the current window. @@ -10308,6 +10542,9 @@ win_getid([{win} [, {tab}]]) *win_getid()* number {tab}. The first tab has number one. Return zero if the window cannot be found. + Can also be used as a |method|: > + GetWinnr()->win_getid() + win_gettype([{nr}]) *win_gettype()* Return the type of the window: "autocmd" autocommand window. Temporary window @@ -10328,20 +10565,32 @@ win_gettype([{nr}]) *win_gettype()* popup window then 'buftype' is "terminal" and win_gettype() returns "popup". + Can also be used as a |method|: > + GetWinid()->win_gettype() +< win_gotoid({expr}) *win_gotoid()* Go to window with ID {expr}. This may also change the current tabpage. Return TRUE if successful, FALSE if the window cannot be found. -win_id2tabwin({expr} *win_id2tabwin()* + Can also be used as a |method|: > + GetWinid()->win_gotoid() + +win_id2tabwin({expr}) *win_id2tabwin()* Return a list with the tab number and window number of window with ID {expr}: [tabnr, winnr]. Return [0, 0] if the window cannot be found. + Can also be used as a |method|: > + GetWinid()->win_id2tabwin() + win_id2win({expr}) *win_id2win()* Return the window number of window with ID {expr}. Return 0 if the window cannot be found in the current tabpage. + Can also be used as a |method|: > + GetWinid()->win_id2win() + win_screenpos({nr}) *win_screenpos()* Return the screen position of window {nr} as a list with two numbers: [row, col]. The first window always has position @@ -10351,6 +10600,9 @@ win_screenpos({nr}) *win_screenpos()* Returns [0, 0] if the window cannot be found in the current tabpage. + Can also be used as a |method|: > + GetWinid()->win_screenpos() +< win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* Move the window {nr} to a new split of the window {target}. This is similar to moving to {target}, creating a new window @@ -10371,6 +10623,9 @@ win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* present, the values of 'splitbelow' and 'splitright' are used. + Can also be used as a |method|: > + GetWinid()->win_splitmove(target) +< *winbufnr()* winbufnr({nr}) The result is a Number, which is the number of the buffer associated with window {nr}. {nr} can be the window number or @@ -10405,6 +10660,9 @@ winheight({nr}) *winheight()* This excludes any window toolbar line. Examples: > :echo "The current window has " . winheight(0) . " lines." + +< Can also be used as a |method|: > + GetWinid()->winheight() < winlayout([{tabnr}]) *winlayout()* The result is a nested List containing the layout of windows @@ -10436,6 +10694,9 @@ winlayout([{tabnr}]) *winlayout()* ['col', [['leaf', 1002], ['row', [['leaf', 1003], ['leaf', 1001]]], ['leaf', 1000]]] < + Can also be used as a |method|: > + GetTabnr()->winlayout() +< *winline()* winline() The result is a Number, which is the screen line of the cursor in the window. This is counting screen lines from the top of @@ -10470,6 +10731,9 @@ winnr([{arg}]) The result is a Number, which is the number of the current let window_count = winnr('$') let prev_window = winnr('#') let wnum = winnr('3k') + +< Can also be used as a |method|: > + GetWinval()->winnr() < *winrestcmd()* winrestcmd() Returns a sequence of |:resize| commands that should restore @@ -10498,6 +10762,9 @@ winrestview({dict}) If you have changed the values the result is unpredictable. If the window size changed the result won't be the same. + Can also be used as a |method|: > + GetView()->winrestview() +< *winsaveview()* winsaveview() Returns a |Dictionary| that contains information to restore the view of the current window. Use |winrestview()| to @@ -10536,6 +10803,8 @@ winwidth({nr}) *winwidth()* < For getting the terminal or screen size, see the 'columns' option. + Can also be used as a |method|: > + GetWinid()->winwidth() wordcount() *wordcount()* The result is a dictionary of byte/chars/word statistics for @@ -10594,17 +10863,19 @@ writefile({object}, {fname} [, {flags}]) :let fl = readfile("foo", "b") :call writefile(fl, "foocopy", "b") +< Can also be used as a |method|: > + GetText()->writefile("thefile") xor({expr}, {expr}) *xor()* Bitwise XOR on the two arguments. The arguments are converted to a number. A List, Dict or Float argument causes an error. Example: > :let bits = xor(bits, 0x80) -< Can also be used as a |method|: > +< + Can also be used as a |method|: > :let bits = bits->xor(0x80) < - *string-match* Matching a pattern in a String diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 42a9993c8c..bbbe71ec3a 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -128,18 +128,19 @@ can be used to overrule the filetype used for certain extensions: file name variable ~ *.asa g:filetype_asa |ft-aspvbs-syntax| |ft-aspperl-syntax| - *.asp g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax| *.asm g:asmsyntax |ft-asm-syntax| - *.prg g:filetype_prg - *.pl g:filetype_pl - *.inc g:filetype_inc - *.w g:filetype_w |ft-cweb-syntax| + *.asp g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax| + *.fs g:filetype_fs |ft-forth-syntax| *.i g:filetype_i |ft-progress-syntax| + *.inc g:filetype_inc *.m g:filetype_m |ft-mathematica-syntax| *.p g:filetype_p |ft-pascal-syntax| + *.pl g:filetype_pl *.pp g:filetype_pp |ft-pascal-syntax| + *.prg g:filetype_prg *.sh g:bash_is_sh |ft-sh-syntax| *.tex g:tex_flavor |ft-tex-plugin| + *.w g:filetype_w |ft-cweb-syntax| *filetype-ignore* To avoid that certain files are being inspected, the g:ft_ignore_pat variable @@ -494,7 +495,6 @@ Options: For further discussion of fortran_have_tabs and the method used for the detection of source format see |ft-fortran-syntax|. - GIT COMMIT *ft-gitcommit-plugin* One command, :DiffGitCached, is provided to show a diff of the current commit diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 6416f49061..173d3c0cdf 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -93,6 +93,7 @@ REFERENCE MANUAL: These files explain every detail of Vim. *reference_toc* General subjects ~ |intro.txt| general introduction to Vim; notation used in help files +|nvim.txt| Transitioning from Vim |help.txt| overview and quick reference (this file) |helphelp.txt| about using the help files |index.txt| alphabetical index of all commands @@ -133,17 +134,19 @@ Advanced editing ~ |api.txt| Nvim API via RPC, Lua and VimL Special issues ~ -|testing.txt| testing Vim and Vim scripts -|print.txt| printing -|remote.txt| using Vim as a server or client +|testing.txt| testing Vim and Vim scripts +|print.txt| printing +|remote_plugin.txt| Nvim support for remote plugins Programming language support ~ |indent.txt| automatic indenting for C and other languages |lsp.txt| Language Server Protocol (LSP) |treesitter.txt| tree-sitter library for incremental parsing of buffers +|diagnostic.txt| Diagnostic framework |syntax.txt| syntax highlighting |filetype.txt| settings done specifically for a type of file |quickfix.txt| commands for a quick edit-compile-fix cycle +|provider.txt| Built-in remote plugin hosts |ft_ada.txt| Ada (the programming language) support |ft_ps1.txt| Filetype plugin for Windows PowerShell |ft_raku.txt| Filetype plugin for Raku @@ -164,6 +167,7 @@ GUI ~ Interfaces ~ |if_cscop.txt| using Cscope with Vim +|if_perl.txt| Perl interface |if_pyth.txt| Python interface |if_ruby.txt| Ruby interface |sign.txt| debugging signs @@ -171,6 +175,16 @@ Interfaces ~ Versions ~ |vim_diff.txt| Main differences between Nvim and Vim |vi_diff.txt| Main differences between Vim and Vi +|deprecated.txt| Deprecated items that have been or will be removed + +Other ~ +|terminal_emulator.txt| Terminal buffers +|term.txt| Terminal UI +|ui.txt| Nvim UI protocol +|channel.txt| Nvim asynchronous IO +|dev_style.txt| Nvim style guide +|job_control.txt| Spawn and control multiple processes + *standard-plugin-list* Standard plugins ~ |matchit.txt| Extended |%| matching diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index aaa2a35fe1..9cc7d063a8 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -164,7 +164,7 @@ If you would like to open the help in the current window, see this tip: The initial height of the help window can be set with the 'helpheight' option (default 20). - + *help-buffer-options* When the help buffer is created, several local options are set to make sure the help text is displayed as it was intended: 'iskeyword' nearly all ASCII chars except ' ', '*', '"' and '|' @@ -319,21 +319,25 @@ Hints for translators: 3. Writing help files *help-writing* For ease of use, a Vim help file for a plugin should follow the format of the -standard Vim help files. If you are writing a new help file it's best to copy -one of the existing files and use it as a template. +standard Vim help files, except for the first line. If you are writing a new +help file it's best to copy one of the existing files and use it as a +template. The first line in a help file should have the following format: -*helpfile_name.txt* For Vim version 7.3 Last change: 2010 June 4 +*plugin_name.txt* {short description of the plugin} + +The first field is a help tag where ":help plugin_name" will jump to. The +remainder of the line, after a Tab, describes the plugin purpose in a short +way. This will show up in the "LOCAL ADDITIONS" section of the main help +file. Check there that it shows up properly: |local-additions|. -The first field is a link to the help file name. The second field describes -the applicable Vim version. The last field specifies the last modification -date of the file. Each field is separated by a tab. +If you want to add a version number or last modification date, put it in the +second line, right aligned. At the bottom of the help file, place a Vim modeline to set the 'textwidth' and 'tabstop' options and the 'filetype' to "help". Never set a global option -in such a modeline, that can have consequences undesired by whoever reads that -help. +in such a modeline, that can have undesired consequences. TAGS diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt deleted file mode 100644 index 34bcf0f039..0000000000 --- a/runtime/doc/if_lua.txt +++ /dev/null @@ -1,8 +0,0 @@ - - - NVIM REFERENCE MANUAL - -Moved to |lua.txt| - -============================================================================== - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index dc46fa515a..fea47de220 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -45,6 +45,11 @@ To see what version of Python you have: > There is no need to "import sys", it's done by default. + *python-environment* +Environment variables set in Vim are not always available in Python. This +depends on how Vim and Python were build. Also see +https://docs.python.org/3/library/os.html#os.environ + Note: Python is very sensitive to indenting. Make sure the "class" line and "EOF" do not have any indent. diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index baa7bc1992..d8689e2c65 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -339,7 +339,6 @@ tag char note action in Normal mode ~ insert text, repeat N times |P| ["x]P 2 put the text [from register x] before the cursor N times -|Q| Q switch to "Ex" mode |R| R 2 enter replace mode: overtype existing characters, repeat the entered text N-1 times @@ -401,6 +400,7 @@ tag char note action in Normal mode ~ |q| q{0-9a-zA-Z"} record typed characters into named register {0-9a-zA-Z"} (uppercase to append) |q| q (while recording) stops recording +|Q| Q replay last recorded macro |q:| q: edit : command-line in command-line window |q/| q/ edit / command-line in command-line window |q?| q? edit ? command-line in command-line window diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index a89263861b..0e0156ac6b 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -563,8 +563,8 @@ The command CTRL-\ CTRL-G or <C-\><C-G> can be used to go to Insert mode when make sure Vim is in the mode indicated by 'insertmode', without knowing in what mode Vim currently is. - *gQ* *Q* *mode-Ex* *Ex-mode* *Ex* *EX* *E501* -Q or gQ Switch to Ex mode. This is like typing ":" commands + *gQ* *mode-Ex* *Ex-mode* *Ex* *EX* *E501* +gQ Switch to Ex mode. This is like typing ":" commands one after another, except: - You don't have to keep pressing ":". - The screen doesn't get updated after each command. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 83d201c23a..3793a21f36 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -521,7 +521,9 @@ buf_request({bufnr}, {method}, {params}, {handler}) {method} (string) LSP method name {params} (optional, table) Parameters to send to the server - {handler} (optional, function) See |lsp-handler| + {handler} (optional, function) See |lsp-handler| If nil, + follows resolution strategy defined in + |lsp-handler-configuration| Return: ~ 2-tuple: @@ -630,9 +632,10 @@ client() *vim.lsp.client* described in |lsp-handler|. • {requests} (table): The current pending requests in flight to the server. Entries are key-value pairs with the key - being the request ID while the value is a table with `type`, - `bufnr`, and `method` key-value pairs. `type` is either "pending" - for an active request, or "cancel" for a cancel request. + being the request ID while the value is a table with + `type` , `bufnr` , and `method` key-value pairs. `type` is + either "pending" for an active request, or "cancel" for a + cancel request. • {config} (table): copy of the table that was passed by the user to |vim.lsp.start_client()|. • {server_capabilities} (table): Response from the server @@ -650,12 +653,34 @@ client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* Return: ~ true if client is stopped, false otherwise. -flush({client}) *vim.lsp.flush()* - TODO: Documentation - *vim.lsp.for_each_buffer_client()* for_each_buffer_client({bufnr}, {fn}) - TODO: Documentation + Invokes a function for each LSP client attached to a buffer. + + Parameters: ~ + {bufnr} number Buffer number + {fn} function Function to run on each client attached + to buffer {bufnr}. The function takes the client, + client ID, and buffer number as arguments. + Example: > + + vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr) + print(vim.inspect(client)) + end) +< + +formatexpr({opts}) *vim.lsp.formatexpr()* + Provides an interface between the built-in client and a + `formatexpr` function. + + Currently only supports a single client. This can be set via `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach` via vim.api.nvim_buf_set_option(bufnr, 'formatexpr , 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`. + + Parameters: ~ + {opts} table options for customizing the formatting + expression which takes the following optional + keys: + • timeout_ms (default 500ms). The timeout period + for the formatting request. get_active_clients() *vim.lsp.get_active_clients()* Gets all active clients. @@ -665,8 +690,10 @@ get_active_clients() *vim.lsp.get_active_clients()* *vim.lsp.get_buffers_by_client_id()* get_buffers_by_client_id({client_id}) + Returns list of buffers attached to client_id. + Parameters: ~ - {client_id} client id + {client_id} number client id Return: ~ list of buffer ids @@ -676,7 +703,7 @@ get_client_by_id({client_id}) *vim.lsp.get_client_by_id()* client may not yet be fully initialized. Parameters: ~ - {client_id} client id number + {client_id} number client id Return: ~ |vim.lsp.client| object, or nil @@ -706,16 +733,6 @@ omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* |complete-items| |CompleteDone| - *vim.lsp.prepare()* -prepare({bufnr}, {firstline}, {new_lastline}, {changedtick}) - TODO: Documentation - -reset({client_id}) *vim.lsp.reset()* - TODO: Documentation - -reset_buf({client}, {bufnr}) *vim.lsp.reset_buf()* - TODO: Documentation - set_log_level({level}) *vim.lsp.set_log_level()* Sets the global log level for LSP logging. @@ -734,15 +751,12 @@ set_log_level({level}) *vim.lsp.set_log_level()* start_client({config}) *vim.lsp.start_client()* Starts and initializes a client with the given configuration. - Parameters `cmd` and `root_dir` are required. + Parameter `cmd` is required. The following parameters describe fields in the {config} table. Parameters: ~ - {root_dir} (string) Directory where the LSP - server will base its rootUri on - initialization. {cmd} (required, string or list treated like |jobstart()|) Base command that initiates the LSP client. @@ -757,6 +771,13 @@ start_client({config}) *vim.lsp.start_client()* { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; } < + {workspace_folders} (table) List of workspace folders + passed to the language server. For + backwards compatibility rootUri and + rootPath will be derived from the + first workspace folder in this list. + See `workspaceFolders` in the LSP + spec. {capabilities} Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|, @@ -776,15 +797,21 @@ start_client({config}) *vim.lsp.start_client()* language server if requested via `workspace/configuration` . Keys are case-sensitive. + {commands} table Table that maps string of + clientside commands to user-defined + functions. Commands passed to + start_client take precedence over the + global command registry. Each key + must be a unique command name, and + the value is a function which is + called if any LSP action (code + action, code lenses, ...) triggers + the command. {init_options} Values to pass in the initialization request as `initializationOptions` . See `initialize` in the LSP spec. {name} (string, default=client-id) Name in log messages. - {workspace_folders} (table) List of workspace folders - passed to the language server. - Defaults to root_dir if not set. See - `workspaceFolders` in the LSP spec {get_language_id} function(bufnr, filetype) -> language ID as string. Defaults to the filetype. @@ -851,6 +878,17 @@ start_client({config}) *vim.lsp.start_client()* notifications to the server by the given number in milliseconds. No debounce occurs if nil + • exit_timeout (number, default 500): + Milliseconds to wait for server to + exit cleanly after sending the + 'shutdown' request before sending + kill -15. If set to false, nvim + exits immediately after sending the + 'shutdown' request to the server. + {root_dir} string Directory where the LSP server + will base its workspaceFolders, + rootUri, and rootPath on + initialization. Return: ~ Client id. |vim.lsp.get_client_by_id()| Note: client may @@ -876,6 +914,23 @@ stop_client({client_id}, {force}) *vim.lsp.stop_client()* thereof {force} boolean (optional) shutdown forcefully +tagfunc({...}) *vim.lsp.tagfunc()* + Provides an interface between the built-in client and + 'tagfunc'. + + When used with normal mode commands (e.g. |CTRL-]|) this will + invoke the "textDocument/definition" LSP method to find the + tag under the cursor. Otherwise, uses "workspace/symbol". If + no results are returned from any LSP servers, falls back to + using built-in tags. + + Parameters: ~ + {pattern} Pattern used to find a workspace symbol + {flags} See |tag-function| + + Return: ~ + A list of matching tags + with({handler}, {override_config}) *vim.lsp.with()* Function to manage overriding defaults for LSP handlers. @@ -942,9 +997,9 @@ document_highlight() *vim.lsp.buf.document_highlight()* triggered by a key mapping or by events such as `CursorHold` , eg: > - vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]] - vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]] - vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]] + autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight() + autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight() + autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references() < Note: Usage of |vim.lsp.buf.document_highlight()| requires the @@ -1012,7 +1067,7 @@ formatting_sync({options}, {timeout_ms}) |vim.lsp.buf_request_sync()|. Example: > - vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()]] + autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync() < Parameters: ~ @@ -1044,9 +1099,6 @@ outgoing_calls() *vim.lsp.buf.outgoing_calls()* cursor in the |quickfix| window. If the symbol can resolve to multiple items, the user can pick one in the |inputlist|. -prepare_rename({err}, {result}) *vim.lsp.buf.prepare_rename()* - TODO: Documentation - *vim.lsp.buf.range_code_action()* range_code_action({context}, {start_pos}, {end_pos}) Performs |vim.lsp.buf.code_action()| for a given range. @@ -1280,12 +1332,14 @@ apply_text_document_edit({text_document_edit}, {index}) https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit *vim.lsp.util.apply_text_edits()* -apply_text_edits({text_edits}, {bufnr}) +apply_text_edits({text_edits}, {bufnr}, {offset_encoding}) Applies a list of text edits to a buffer. Parameters: ~ - {text_edits} table list of `TextEdit` objects - {bufnr} number Buffer id + {text_edits} table list of `TextEdit` objects + {bufnr} number Buffer id + {offset_encoding} string utf-8|utf-16|utf-32|nil defaults + to encoding of first client of `bufnr` See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit @@ -1301,68 +1355,41 @@ buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* Removes document highlights from a buffer. Parameters: ~ - {bufnr} buffer id + {bufnr} number Buffer id *vim.lsp.util.buf_highlight_references()* -buf_highlight_references({bufnr}, {references}) +buf_highlight_references({bufnr}, {references}, {offset_encoding}) Shows a list of document highlights for a certain buffer. Parameters: ~ - {bufnr} buffer id - {references} List of `DocumentHighlight` objects to - highlight + {bufnr} number Buffer id + {references} table List of `DocumentHighlight` + objects to highlight + {offset_encoding} string One of "utf-8", "utf-16", + "utf-32", or nil. Defaults to + `offset_encoding` of first client of + `bufnr` See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight -buf_lines({bufnr}) *vim.lsp.util.buf_lines()* - TODO: Documentation - *vim.lsp.util.character_offset()* -character_offset({bufnr}, {row}, {col}) +character_offset({buf}, {row}, {col}, {offset_encoding}) Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer. Parameters: ~ - {buf} buffer id (0 for current) - {row} 0-indexed line - {col} 0-indexed byte offset in line + {buf} buffer id (0 for current) + {row} 0-indexed line + {col} 0-indexed byte offset in line + {offset_encoding} string utf-8|utf-16|utf-32|nil defaults + to `offset_encoding` of first client of + `buf` Return: ~ - (number, number) UTF-32 and UTF-16 index of the character + (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf} - *vim.lsp.util.close_preview_autocmd()* -close_preview_autocmd({events}, {winnr}) - Creates autocommands to close a preview window when events - happen. - - Parameters: ~ - {events} (table) list of events - {winnr} (number) window id of preview window - - See also: ~ - |autocmd-events| - - *vim.lsp.util.compute_diff()* -compute_diff({old_lines}, {new_lines}, {start_line_idx}, {end_line_idx}, - {offset_encoding}) - Returns the range table for the difference between old and new - lines - - Parameters: ~ - {old_lines} table list of lines - {new_lines} table list of lines - {start_line_idx} int line to begin search for first - difference - {end_line_idx} int line to begin search for last - difference - {offset_encoding} string encoding requested by language - server - - Return: ~ - table start_line_idx and start_col_idx of range - *vim.lsp.util.convert_input_to_markdown_lines()* convert_input_to_markdown_lines({input}, {contents}) Converts any of `MarkedString` | `MarkedString[]` | @@ -1403,12 +1430,6 @@ convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers}) See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp -create_file({change}) *vim.lsp.util.create_file()* - TODO: Documentation - -delete_file({change}) *vim.lsp.util.delete_file()* - TODO: Documentation - *vim.lsp.util.extract_completion_items()* extract_completion_items({result}) Can be used to extract the completion items from a `textDocument/completion` request, which may return one of `CompletionItem[]` , `CompletionList` or null. @@ -1436,29 +1457,6 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()* See also: ~ |softtabstop| -get_line({uri}, {row}) *vim.lsp.util.get_line()* - Gets the zero-indexed line from the given uri. - - Parameters: ~ - {uri} string uri of the resource to get the line from - {row} number zero-indexed line number - - Return: ~ - string the line at row in filename - -get_lines({uri}, {rows}) *vim.lsp.util.get_lines()* - Gets the zero-indexed lines from the given uri. - - Parameters: ~ - {uri} string uri of the resource to get the lines from - {rows} number[] zero-indexed line numbers - - Return: ~ - table<number string> a table mapping rows to lines - -get_progress_messages() *vim.lsp.util.get_progress_messages()* - TODO: Documentation - jump_to_location({location}) *vim.lsp.util.jump_to_location()* Jumps to a location. @@ -1531,47 +1529,73 @@ make_formatting_params({options}) https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting *vim.lsp.util.make_given_range_params()* -make_given_range_params({start_pos}, {end_pos}) +make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding}) Using the given range in the current buffer, creates an object that is similar to |vim.lsp.util.make_range_params()|. Parameters: ~ - {start_pos} ({number, number}, optional) mark-indexed - position. Defaults to the start of the last - visual selection. - {end_pos} ({number, number}, optional) mark-indexed - position. Defaults to the end of the last - visual selection. + {start_pos} ({number, number}, optional) + mark-indexed position. Defaults to the + start of the last visual selection. + {end_pos} ({number, number}, optional) + mark-indexed position. Defaults to the + end of the last visual selection. + {bufnr} (optional, number): buffer handle or 0 + for current, defaults to current + {offset_encoding} string utf-8|utf-16|utf-32|nil defaults + to `offset_encoding` of first client of + `bufnr` Return: ~ { textDocument = { uri = `current_file_uri` }, range = { start = `start_position` , end = `end_position` } } -make_position_params() *vim.lsp.util.make_position_params()* + *vim.lsp.util.make_position_params()* +make_position_params({window}, {offset_encoding}) Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. + Parameters: ~ + {window} (optional, number): window handle or 0 + for current, defaults to current + {offset_encoding} string utf-8|utf-16|utf-32|nil defaults + to `offset_encoding` of first client of + buffer of `window` + Return: ~ `TextDocumentPositionParams` object See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams -make_range_params() *vim.lsp.util.make_range_params()* + *vim.lsp.util.make_range_params()* +make_range_params({window}, {offset_encoding}) Using the current position in the current buffer, creates an object that can be used as a building block for several LSP requests, such as `textDocument/codeAction` , `textDocument/colorPresentation` , `textDocument/rangeFormatting` . + Parameters: ~ + {window} (optional, number): window handle or 0 + for current, defaults to current + {offset_encoding} string utf-8|utf-16|utf-32|nil defaults + to `offset_encoding` of first client of + buffer of `window` + Return: ~ { textDocument = { uri = `current_file_uri` }, range = { start = `current_position` , end = `current_position` } } -make_text_document_params() *vim.lsp.util.make_text_document_params()* + *vim.lsp.util.make_text_document_params()* +make_text_document_params({bufnr}) Creates a `TextDocumentIdentifier` object for the current buffer. + Parameters: ~ + {bufnr} (optional, number): Buffer handle, defaults to + current + Return: ~ `TextDocumentIdentifier` @@ -1593,25 +1617,32 @@ open_floating_preview({contents}, {syntax}, {opts}) Parameters: ~ {contents} table of lines to show in window {syntax} string of syntax to set for opened buffer - {opts} dictionary with optional fields - • height of floating window - • width of floating window - • wrap boolean enable wrapping of long lines - (defaults to true) - • wrap_at character to wrap at for computing - height when wrap is enabled - • max_width maximal width of floating window - • max_height maximal height of floating window - • pad_top number of lines to pad contents at - top - • pad_bottom number of lines to pad contents - at bottom - • focus_id if a popup with this id is opened, - then focus it - • close_events list of events that closes the + {opts} table with optional fields (additional keys + are passed on to |vim.api.nvim_open_win()|) + • height: (number) height of floating window + • width: (number) width of floating window + • wrap: (boolean, default true) wrap long + lines + • wrap_at: (string) character to wrap at for + computing height when wrap is enabled + • max_width: (number) maximal width of + floating window + • max_height: (number) maximal height of floating window - • focusable (boolean, default true): Make + • pad_top: (number) number of lines to pad + contents at top + • pad_bottom: (number) number of lines to pad + contents at bottom + • focus_id: (string) if a popup with this id + is opened, then focus it + • close_events: (table) list of events that + closes the floating window + • focusable: (boolean, default true) Make float focusable + • focus: (boolean, default true) If `true` , + and if {focusable} is also `true` , focus an + existing floating window with the same + {focus_id} Return: ~ bufnr,winnr buffer and window number of the newly created @@ -1752,7 +1783,10 @@ get_filename() *vim.lsp.log.get_filename()* (string) log filename get_level() *vim.lsp.log.get_level()* - TODO: Documentation + Gets the current log level. + + Return: ~ + string current log level set_format_func({handle}) *vim.lsp.log.set_format_func()* Sets formatting function used to format logs @@ -1800,14 +1834,19 @@ notify({method}, {params}) *vim.lsp.rpc.notify()* (bool) `true` if notification could be sent, `false` if not -request({method}, {params}, {callback}) *vim.lsp.rpc.request()* + *vim.lsp.rpc.request()* +request({method}, {params}, {callback}, {notify_reply_callback}) Sends a request to the LSP server and runs {callback} upon response. Parameters: ~ - {method} (string) The invoked LSP method - {params} (table) Parameters for the invoked LSP method - {callback} (function) Callback to invoke + {method} (string) The invoked LSP method + {params} (table) Parameters for the + invoked LSP method + {callback} (function) Callback to invoke + {notify_reply_callback} (function) Callback to invoke as + soon as a request is no longer + pending Return: ~ (bool, number) `(true, message_id)` if request could be @@ -1826,7 +1865,8 @@ rpc_response_error({code}, {message}, {data}) *vim.lsp.rpc.start()* start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params}) Starts an LSP server process and create an LSP RPC client - object to interact with it. + object to interact with it. Communication with the server is + currently limited to stdio. Parameters: ~ {cmd} (string) Command to start the LSP @@ -1862,6 +1902,31 @@ start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params}) ============================================================================== +Lua module: vim.lsp.sync *lsp-sync* + + *vim.lsp.sync.compute_diff()* +compute_diff({prev_lines}, {curr_lines}, {firstline}, {lastline}, + {new_lastline}, {offset_encoding}, {line_ending}) + Returns the range table for the difference between prev and + curr lines + + Parameters: ~ + {prev_lines} table list of lines + {curr_lines} table list of lines + {firstline} number line to begin search for first + difference + {lastline} number line to begin search in + old_lines for last difference + {new_lastline} number line to begin search in + new_lines for last difference + {offset_encoding} string encoding requested by language + server + + Return: ~ + table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent + + +============================================================================== Lua module: vim.lsp.protocol *lsp-protocol* *vim.lsp.protocol.make_client_capabilities()* @@ -1871,14 +1936,14 @@ make_client_capabilities() *vim.lsp.protocol.resolve_capabilities()* resolve_capabilities({server_capabilities}) - `*` to match one or more characters in a path segment `?` to - match on one character in a path segment `**` to match any - number of path segments, including none `{}` to group - conditions (e.g. `**/*.{ts,js}` matches all TypeScript and - JavaScript files) `[]` to declare a range of characters to - match in a path segment (e.g., `example.[0-9]` to match on - `example.0` , `example.1` , …) `[!...]` to negate a range of - characters to match in a path segment (e.g., `example.[!0-9]` - to match on `example.a` , `example.b` , but not `example.0` ) + Creates a normalized object describing LSP server + capabilities. + + Parameters: ~ + {server_capabilities} table Table of capabilities + supported by the server + + Return: ~ + table Normalized table of capabilities vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index ef2d87949d..630df16e79 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -362,8 +362,8 @@ cases there is the following agreement: converted to a dictionary `{'a': 42}`: non-string keys are ignored. Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. are errors. - - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well - as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not + - `{[vim.type_idx]=vim.types.array}` is converted to an empty list. As well + as `{[vim.type_idx]=vim.types.array, [42]=1}`: integral keys that do not form a 1-step sequence from 1 to N are ignored, as well as all non-integral keys. @@ -1190,9 +1190,6 @@ defer_fn({fn}, {timeout}) *vim.defer_fn()* Return: ~ timer luv timer object -insert_keys({obj}) *vim.insert_keys()* - TODO: Documentation - inspect({object}, {options}) *vim.inspect()* Return a human-readable representation of the given object. @@ -1200,20 +1197,16 @@ inspect({object}, {options}) *vim.inspect()* https://github.com/kikito/inspect.lua https://github.com/mpeterv/vinspect -make_dict_accessor({scope}) *vim.make_dict_accessor()* - TODO: Documentation - -notify({msg}, {log_level}, {_opts}) *vim.notify()* +notify({msg}, {log_level}, {opts}) *vim.notify()* Notification provider Without a runtime, writes to :Messages Parameters: ~ - {msg} Content of the notification to show to the - user - {log_level} Optional log level - {opts} Dictionary with optional options (timeout, - etc) + {msg} string Content of the notification to show to + the user + {log_level} number|nil enum from vim.log.levels + {opts} table|nil additional options (timeout, etc) See also: ~ :help nvim_notify @@ -1239,7 +1232,7 @@ on_key({fn}, {ns_id}) *vim.on_key()* it removes the callback for the associated {ns_id} {ns_id} number? Namespace ID. If nil or 0, generates and - returns a new |nvim_create_namesapce()| id. + returns a new |nvim_create_namespace()| id. Return: ~ number Namespace id associated with {fn}. Or count of all @@ -1328,7 +1321,7 @@ deepcopy({orig}) *vim.deepcopy()* and will throw an error. Parameters: ~ - {orig} Table to copy + {orig} table Table to copy Return: ~ New table of copied keys and (nested) values. @@ -1369,9 +1362,6 @@ is_callable({f}) *vim.is_callable()* Return: ~ true if `f` is callable, else false -is_valid({opt}) *vim.is_valid()* - TODO: Documentation - list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* Extends a list-like table with the values of another list-like table. @@ -1617,14 +1607,12 @@ validate({opt}) *vim.validate()* vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} => NOP (success) -< -> - vim.validate{arg1={1, 'table'}} - => error('arg1: expected table, got number') -< -> - vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} - => error('arg1: expected even number, got 3') + + vim.validate{arg1={1, 'table'}} + => error('arg1: expected table, got number') + + vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} + => error('arg1: expected even number, got 3') < Parameters: ~ @@ -1658,40 +1646,38 @@ uri_from_bufnr({bufnr}) *vim.uri_from_bufnr()* Get a URI from a bufnr Parameters: ~ - {bufnr} (number): Buffer number + {bufnr} number Return: ~ - URI + string URI uri_from_fname({path}) *vim.uri_from_fname()* Get a URI from a file path. Parameters: ~ - {path} (string): Path to file + {path} string Path to file Return: ~ - URI + string URI uri_to_bufnr({uri}) *vim.uri_to_bufnr()* - Return or create a buffer for a uri. + Get the buffer for a uri. Creates a new unloaded buffer if no + buffer for the uri already exists. Parameters: ~ - {uri} (string): The URI + {uri} string Return: ~ - bufnr. - - Note: - Creates buffer but does not load it + number bufnr uri_to_fname({uri}) *vim.uri_to_fname()* Get a filename from a URI Parameters: ~ - {uri} (string): The URI + {uri} string Return: ~ - Filename + string filename or unchanged URI for non-file URIs ============================================================================== @@ -1731,6 +1717,12 @@ select({items}, {opts}, {on_choice}) *vim.ui.select()* • format_item (function item -> text) Function to format an individual item from `items` . Defaults to `tostring` . + • kind (string|nil) Arbitrary hint string + indicating the item shape. Plugins + reimplementing `vim.ui.select` may wish to + use this to infer the structure or + semantics of `items` , or the context in + which select() was called. {on_choice} function ((item|nil, idx|nil) -> ()) Called once the user made a choice. `idx` is the 1-based index of `item` within `item` . `nil` diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 90d4c4de93..6ad5622893 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -46,6 +46,8 @@ modes. where the map command applies. The result, including {rhs}, is then further scanned for mappings. This allows for nested and recursive use of mappings. + Note: Trailing spaces are included in the {rhs}, + because space is a valid Normal mode command. *:nore* *:norem* :no[remap] {lhs} {rhs} |mapmode-nvo| *:no* *:noremap* *:nor* @@ -79,6 +81,9 @@ modes. Remove the mapping of {lhs} for the modes where the map command applies. The mapping may remain defined for other modes where it applies. + It also works when {lhs} matches the {rhs} of a + mapping. This is for when when an abbreviation + applied. Note: Trailing spaces are included in the {lhs}. This unmap does NOT work: > :map @@ foo @@ -153,7 +158,7 @@ type "a", then "bar" will get inserted. "<unique>" can be used in any order. They must appear right after the command, before any other arguments. - *:map-local* *:map-<buffer>* *E224* *E225* + *:map-local* *:map-<buffer>* *:map-buffer* *E224* *E225* If the first argument to one of these commands is "<buffer>" the mapping will be effective in the current buffer only. Example: > :map <buffer> ,w /[.,;]<CR> @@ -206,7 +211,7 @@ Note: ":map <script>" and ":noremap <script>" do the same thing. The "<script>" overrules the command name. Using ":noremap <script>" is preferred, because it's clearer that remapping is (mostly) disabled. - *:map-<unique>* *E226* *E227* + *:map-<unique>* *:map-unique* *E226* *E227* If the first argument to one of these commands is "<unique>" and it is used to define a new mapping or abbreviation, the command will fail if the mapping or abbreviation already exists. Example: > @@ -318,6 +323,8 @@ Note: - For the same reason, |keycodes| like <C-R><C-W> are interpreted as plain, unmapped keys. - The command is not echo'ed, no need for <silent>. +- The {rhs} is not subject to abbreviations nor to other mappings, even if the + mapping is recursive. - In Visual mode you can use `line('v')` and `col('v')` to get one end of the Visual area, the cursor is at the other end. - In select-mode, |:map| and |:vmap| command mappings are executed in @@ -1419,6 +1426,7 @@ Possible values are (second column is the short name used in listing): Special cases ~ *:command-bang* *:command-bar* *:command-register* *:command-buffer* + *:command-keepscript* There are some special cases as well: -bang The command can take a ! modifier (like :q or :w) diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt deleted file mode 100644 index 5368cf0f4f..0000000000 --- a/runtime/doc/msgpack_rpc.txt +++ /dev/null @@ -1,8 +0,0 @@ - - - NVIM REFERENCE MANUAL - -This document was merged into |api.txt| and |develop.txt|. - -============================================================================== - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 399a59251a..b75dd78b57 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -684,9 +684,12 @@ A jump table for the options with a short description can be found at |Q_op|. 'autowrite' 'aw' boolean (default off) global Write the contents of the file, if it has been modified, on each - :next, :rewind, :last, :first, :previous, :stop, :suspend, :tag, :!, - :make, CTRL-] and CTRL-^ command; and when a :buffer, CTRL-O, CTRL-I, - '{A-Z0-9}, or `{A-Z0-9} command takes one to another file. + `:next`, `:rewind`, `:last`, `:first`, `:previous`, `:stop`, + `:suspend`, `:tag`, `:!`, `:make`, CTRL-] and CTRL-^ command; and when + a :buffer, CTRL-O, CTRL-I, '{A-Z0-9}, or `{A-Z0-9} command takes one + to another file. + A buffer is not written if it becomes hidden, e.g. when 'bufhidden' is + set to "hide" and `:next` is used Note that for some commands the 'autowrite' option is not used, see 'autowriteall' for that. Some buffers will not be written, specifically when 'buftype' is @@ -950,7 +953,6 @@ A jump table for the options with a short description can be found at |Q_op|. error Other Error occurred (e.g. try to join last line) (mostly used in |Normal-mode| or |Cmdline-mode|). esc hitting <Esc> in |Normal-mode|. - ex In |Visual-mode|, hitting |Q| results in an error. hangul Ignored. insertmode Pressing <Esc> in 'insertmode'. lang Calling the beep module for Lua/Mzscheme/TCL. @@ -3226,10 +3228,14 @@ A jump table for the options with a short description can be found at |Q_op|. 'inccommand' 'icm' string (default "nosplit") global - "nosplit": Shows the effects of a command incrementally, as you type. - "split" : Also shows partial off-screen results in a preview window. + When nonempty, shows the effects of |:substitute|, |:smagic|, and + |:snomagic| as you type. - Works for |:substitute|, |:smagic|, |:snomagic|. |hl-Substitute| + Possible values: + nosplit Shows the effects of a command incrementally in the + buffer. + split Like "nosplit", but also shows partial off-screen + results in a preview window. If the preview is too slow (exceeds 'redrawtime') then 'inccommand' is automatically disabled until |Command-line-mode| is done. @@ -3792,6 +3798,13 @@ A jump table for the options with a short description can be found at |Q_op|. The characters ':' and ',' should not be used. UTF-8 characters can be used. All characters must be single width. + Each character can be specified as hex: > + set listchars=eol:\\x24 + set listchars=eol:\\u21b5 + set listchars=eol:\\U000021b5 +< Note that a double backslash is used. The number of hex characters + must be exactly 2 for \\x, 4 for \\u and 8 for \\U. + Examples: > :set lcs=tab:>-,trail:- :set lcs=tab:>-,eol:<,nbsp:% diff --git a/runtime/doc/pi_msgpack.txt b/runtime/doc/pi_msgpack.txt index 951b897f55..1dbd268038 100644 --- a/runtime/doc/pi_msgpack.txt +++ b/runtime/doc/pi_msgpack.txt @@ -68,7 +68,7 @@ msgpack#strftime({format}, {msgpack-integer}) *msgpack#strftime()* *msgpack#strptime* msgpack#strptime({format}, {time}) *msgpack#strptime()* - Reverse of |msgpack#strptime()|: for any time and format + Reverse of |msgpack#strftime()|: for any time and format |msgpack#equal|( |msgpack#strptime|(format, |msgpack#strftime|(format, time)), time) be true. Requires |+python| or |+python3|, without it only supports non-|msgpack-special-dict| nonnegative times and format diff --git a/runtime/doc/pi_zip.txt b/runtime/doc/pi_zip.txt index c715644847..2bbd6eea06 100644 --- a/runtime/doc/pi_zip.txt +++ b/runtime/doc/pi_zip.txt @@ -102,6 +102,9 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright* ============================================================================== 4. History *zip-history* {{{1 + v32 Oct 22, 2021 * to avoid an issue with a vim 8.2 patch, zipfile: has + been changed to zipfile:// . This often shows up + as zipfile:/// with zipped files that are root-based. v29 Apr 02, 2017 * (Klartext) reported that an encrypted zip file could opened but the swapfile held unencrypted contents. The solution is to edit the contents of a zip file diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 9c1f584415..ba1da209f7 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -837,9 +837,9 @@ lists. They set one of the existing error lists as the current one. *:chistory* *:chi* :[count]chi[story] Show the list of error lists. The current list is marked with ">". The output looks like: - error list 1 of 3; 43 errors ~ - > error list 2 of 3; 0 errors ~ - error list 3 of 3; 15 errors ~ + error list 1 of 3; 43 errors :make ~ + > error list 2 of 3; 0 errors :helpgrep tag ~ + error list 3 of 3; 15 errors :grep ex_help *.c ~ When [count] is given, then the count'th quickfix list is made the current list. Example: > diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 8cabf05053..a229c8742f 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -489,6 +489,7 @@ In Insert or Command-line mode: |q| q{a-z} record typed characters into register {a-z} |q| q{A-Z} record typed characters, appended to register {a-z} |q| q stop recording +|Q| Q replay last recorded macro |@| N @{a-z} execute the contents of register {a-z} (N times) |@@| N @@ repeat previous @{a-z} (N times) |:@| :@{a-z} execute the contents of register {a-z} as an Ex @@ -997,7 +998,7 @@ Short explanation of each option: *option-list* |:version| :ve[rsion] show version information |:normal| :norm[al][!] {commands} execute Normal mode commands -|Q| Q switch to "Ex" mode +|gQ| gQ switch to "Ex" mode |:redir| :redir >{file} redirect messages to {file} |:silent| :silent[!] {command} execute {command} silently diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 7e8d93aa71..c7481ad290 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -103,7 +103,7 @@ Which is two characters shorter! When using "global" in Ex mode, a special case is using ":visual" as a command. This will move to a matching line, go to Normal mode to let you -execute commands there until you use |Q| to return to Ex mode. This will be +execute commands there until you use |gQ| to return to Ex mode. This will be repeated for each matching line. While doing this you cannot use ":global". To abort this type CTRL-C twice. @@ -147,6 +147,10 @@ q Stops recording. *@@* *E748* @@ Repeat the previous @{0-9a-z":*} [count] times. + *Q* +Q Repeat the last recorded register [count] times. + See |reg_recorded()|. + *:@* :[addr]@{0-9a-z".=*+} Execute the contents of register {0-9a-z".=*+} as an Ex command. First set cursor at line [addr] (default is diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 5079d900c9..68165f3d3d 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -47,6 +47,8 @@ The color of the column is set with the SignColumn highlight group :highlight SignColumn guibg=darkgrey < +If 'cursorline' is enabled, then the CursorLineSign highlight group is used +|hl-CursorLineSign|. *sign-identifier* Each placed sign is identified by a number called the sign identifier. This identifier is used to jump to the sign or to remove the sign. The identifier @@ -131,6 +133,10 @@ See |sign_define()| for the equivalent Vim script function. texthl={group} Highlighting group used for the text item. + culhl={group} + Highlighting group used for the text item when the cursor is + on the same line as the sign and 'cursorline' is enabled. + Example: > :sign define MySign text=>> texthl=Search linehl=DiffText < @@ -377,6 +383,9 @@ sign_define({list}) text text that is displayed when there is no icon or the GUI is not being used. texthl highlight group used for the text item + culhl highlight group used for the text item when + the cursor is on the same line as the sign and + 'cursorline' is enabled. numhl highlight group used for 'number' column at the associated line. Overrides |hl-LineNr|, |hl-CursorLineNr|. @@ -404,6 +413,9 @@ sign_define({list}) \ 'text' : '!!'} \ ]) < + Can also be used as a |method|: > + GetSignList()->sign_define() + sign_getdefined([{name}]) *sign_getdefined()* Get a list of defined signs and their attributes. This is similar to the |:sign-list| command. @@ -416,14 +428,19 @@ sign_getdefined([{name}]) *sign_getdefined()* following entries: icon full path to the bitmap file of the sign linehl highlight group used for the whole line the - sign is placed in. + sign is placed in; not present if not set. name name of the sign text text that is displayed when there is no icon or the GUI is not being used. - texthl highlight group used for the text item + texthl highlight group used for the text item; not + present if not set. + culhl highlight group used for the text item when + the cursor is on the same line as the sign and + 'cursorline' is enabled; not present if not + set. numhl highlight group used for 'number' column at the associated line. Overrides |hl-LineNr|, - |hl-CursorLineNr|. + |hl-CursorLineNr|; not present if not set. Returns an empty List if there are no signs and when {name} is not found. @@ -435,6 +452,9 @@ sign_getdefined([{name}]) *sign_getdefined()* " Get the attribute of the sign named mySign echo sign_getdefined("mySign") < + Can also be used as a |method|: > + GetSignList()->sign_getdefined() + sign_getplaced([{buf} [, {dict}]]) *sign_getplaced()* Return a list of signs placed in a buffer or all the buffers. This is similar to the |:sign-place-list| command. @@ -495,6 +515,9 @@ sign_getplaced([{buf} [, {dict}]]) *sign_getplaced()* " Get a List of all the placed signs echo sign_getplaced() < + Can also be used as a |method|: > + GetBufname()->sign_getplaced() +< *sign_jump()* sign_jump({id}, {group}, {buf}) Open the buffer {buf} or jump to the window that contains @@ -510,7 +533,9 @@ sign_jump({id}, {group}, {buf}) " Jump to sign 10 in the current buffer call sign_jump(10, '', '') < - + Can also be used as a |method|: > + GetSignid()->sign_jump() +< *sign_place()* sign_place({id}, {group}, {name}, {buf} [, {dict}]) Place the sign defined as {name} at line {lnum} in file or @@ -560,7 +585,9 @@ sign_place({id}, {group}, {name}, {buf} [, {dict}]) call sign_place(10, 'g3', 'sign4', 'json.c', \ {'lnum' : 40, 'priority' : 90}) < - + Can also be used as a |method|: > + GetSignid()->sign_place(group, name, expr) +< *sign_placelist()* sign_placelist({list}) Place one or more signs. This is similar to the @@ -620,6 +647,8 @@ sign_placelist({list}) \ 'lnum' : 50} \ ]) < + Can also be used as a |method|: > + GetSignlist()->sign_placelist() sign_undefine([{name}]) *sign_undefine()* sign_undefine({list}) @@ -644,6 +673,8 @@ sign_undefine({list}) " Delete all the signs call sign_undefine() < + Can also be used as a |method|: > + GetSignlist()->sign_undefine() sign_unplace({group} [, {dict}]) *sign_unplace()* Remove a previously placed sign in one or more buffers. This @@ -686,6 +717,9 @@ sign_unplace({group} [, {dict}]) *sign_unplace()* " Remove all the placed signs from all the buffers call sign_unplace('*') + +< Can also be used as a |method|: > + GetSigngroup()->sign_unplace() < sign_unplacelist({list}) *sign_unplacelist()* Remove previously placed signs from one or more buffers. This @@ -715,5 +749,8 @@ sign_unplacelist({list}) *sign_unplacelist()* \ {'id' : 20, 'buffer' : 'b.vim'}, \ ]) < + Can also be used as a |method|: > + GetSignlist()->sign_unplacelist() +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index e423c59efe..d49809599d 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1497,6 +1497,14 @@ gvim display. Here, statements are colored LightYellow instead of Yellow, and conditionals are LightBlue for better distinction. +FORTH *forth.vim* *ft-forth-syntax* + +Files matching "*.fs" could be F# or Forth. If the automatic detection +doesn't work for you, or you don't edit F# at all, use this in your +startup vimrc: > + :let filetype_fs = "forth" + + FORTRAN *fortran.vim* *ft-fortran-syntax* Default highlighting and dialect ~ @@ -3153,6 +3161,14 @@ buffer by buffer basis. For more detailed instructions see |ft_sql.txt|. +SQUIRREL *squirrel.vim* *ft-squirrel-syntax* + +Squirrel is a high level imperative, object-oriented programming language, +designed to be a light-weight scripting language that fits in the size, memory +bandwidth, and real-time requirements of applications like video games. Files +with the following extensions are recognized as squirrel files: .nut. + + TCSH *tcsh.vim* *ft-tcsh-syntax* This covers the shell named "tcsh". It is a superset of csh. See |csh.vim| @@ -5111,6 +5127,10 @@ LineNrBelow Line number for when the 'relativenumber' *hl-CursorLineNr* CursorLineNr Like LineNr when 'cursorline' is set and 'cursorlineopt' contains "number" or is "both", for the cursor line. + *hl-CursorLineSign* +CursorLineSign Like SignColumn when 'cursorline' is set for the cursor line. + *hl-CursorLineFold* +CursorLineFold Like FoldColumn when 'cursorline' is set for the cursor line. *hl-MatchParen* MatchParen The character under the cursor or just before it, if it is a paired bracket, and its match. |pi_paren.txt| diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt index f0bda5aaf8..8ec66d26a4 100644 --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -157,6 +157,9 @@ assert_nobeep({cmd}) *assert_nobeep()* produces a beep or visual bell. Also see |assert_beeps()|. + Can also be used as a |method|: > + GetCmd()->assert_nobeep() +< *assert_notequal()* assert_notequal({expected}, {actual} [, {msg}]) The opposite of `assert_equal()`: add an error message to diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 441ae13df4..8f7241dd46 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -353,7 +353,7 @@ Lua module: vim.treesitter *lua-treesitter-core* get_parser({bufnr}, {lang}, {opts}) *get_parser()* Gets the parser for this bufnr / ft combination. - If needed this will create the parser. Unconditionnally attach + If needed this will create the parser. Unconditionally attach the provided callback Parameters: ~ @@ -380,7 +380,7 @@ Lua module: vim.treesitter.language *treesitter-language* inspect_language({lang}) *inspect_language()* Inspects the provided language. - Inspecting provides some useful informations on the language + Inspecting provides some useful information on the language like node names, ... Parameters: ~ @@ -421,9 +421,9 @@ get_node_text({node}, {source}) *get_node_text()* Gets the text corresponding to a given node Parameters: ~ - {node} the node - {bsource} The buffer or string from which the node is - extracted + {node} the node + {source} The buffer or string from which the node is + extracted get_query({lang}, {query_name}) *get_query()* Returns the runtime query {query_name} for {lang}. @@ -479,7 +479,7 @@ Query:iter_captures({self}, {node}, {source}, {start}, {stop}) {source} is needed if the query contains predicates, then the caller must ensure to use a freshly parsed tree consistent - with the current text of the buffer (if relevent). {start_row} + with the current text of the buffer (if relevant). {start_row} and {end_row} can be used to limit matches inside a row range (this is typically used with root node as the node, i e to get syntax highlight matches in the current viewport). When @@ -503,8 +503,7 @@ Query:iter_captures({self}, {node}, {source}, {start}, {stop}) Parameters: ~ {node} The node under which the search will occur - {source} The source buffer or string to exctract text - from + {source} The source buffer or string to extract text from {start} The starting line of the search {stop} The stopping line of the search (end-exclusive) {self} @@ -531,11 +530,9 @@ Query:iter_matches({self}, {node}, {source}, {start}, {stop}) for id, node in pairs(match) do local name = query.captures[id] -- `node` was captured by the `name` capture in the match -< -> - local node_data = metadata[id] -- Node level metadata -< -> + + local node_data = metadata[id] -- Node level metadata + ... use the info here ... end end @@ -611,7 +608,7 @@ LanguageTree:children({self}) *LanguageTree:children()* {self} LanguageTree:contains({self}, {range}) *LanguageTree:contains()* - Determines wether This goes down the tree to recursively check childs. + Determines whether This goes down the tree to recursively check children. Parameters: ~ {range} is contained in this language tree diff --git a/runtime/doc/usr_20.txt b/runtime/doc/usr_20.txt index 29252705d6..6a8836c8e8 100644 --- a/runtime/doc/usr_20.txt +++ b/runtime/doc/usr_20.txt @@ -289,11 +289,11 @@ In chapter 3 we briefly mentioned the history. The basics are that you can use the <Up> key to recall an older command line. <Down> then takes you back to newer commands. -There are actually four histories. The ones we will mention here are for ":" +There are actually five histories. The ones we will mention here are for ":" commands and for "/" and "?" search commands. The "/" and "?" commands share -the same history, because they are both search commands. The two other -histories are for expressions and input lines for the input() function. -|cmdline-history| +the same history, because they are both search commands. The three other +histories are for expressions, debug mode commands and input lines for the +input() function. |cmdline-history| Suppose you have done a ":set" command, typed ten more colon commands and then want to repeat that ":set" command again. You could press ":" and then ten diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index b0e0bdcb84..8a4468a130 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -387,7 +387,7 @@ g8 Print the hex values of the bytes used in the |:marks| - filter by text in the current file, or file name for other files |:oldfiles| - filter by file name - |:set| - filter by variable name + |:set| - filter by option name Only normal messages are filtered, error messages are not. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 77bf1d29eb..4fcaf15717 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -180,13 +180,15 @@ Commands: |:match| can be invoked before highlight group is defined Events: + |RecordingEnter| + |RecordingLeave| + |SearchWrapped| |Signal| |TabNewEntered| |TermClose| |TermOpen| |UIEnter| |UILeave| - |WinClosed| Functions: |dictwatcheradd()| notifies a callback whenever a |Dict| is modified @@ -356,7 +358,8 @@ Motion: The |jumplist| avoids useless/phantom jumps. Normal commands: - |Q| is the same as |gQ| + |Q| replays the last recorded macro instead of switching to Ex mode. + Instead |gQ| can be used to enter Ex mode. Options: 'ttimeout', 'ttimeoutlen' behavior was simplified @@ -429,7 +432,7 @@ Working directory (Vim implemented some of these later than Nvim): - |DirChanged| can be triggered when switching to another window. - |getcwd()| and |haslocaldir()| may throw errors if the tab page or window cannot be found. *E5000* *E5001* *E5002* -- |haslocaldir()| only checks for tab-local directory when -1 is passed as +- |haslocaldir()| checks for tab-local directory if and only if -1 is passed as window number, and its only possible returns values are 0 and 1. - `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global working directory. Use `getcwd(-1, -1)` to get the global working directory. diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index 111588e43a..4a69fc989b 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -360,7 +360,8 @@ same amount of text as the last time: last line the same number of characters as in the last line the last time. The start of the text is the Cursor position. If the "$" command was used as one of the last commands to extend the highlighted text, the repeating will -be applied up to the rightmost column of the longest line. +be applied up to the rightmost column of the longest line. Any count passed +to the `.` command is not used. ============================================================================== diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 75354968e9..57aaeab766 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Oct 03 +" Last Change: 2021 Dec 14 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -119,7 +119,7 @@ au BufNewFile,BufRead *.aml setf aml " APT config file au BufNewFile,BufRead apt.conf setf aptconf au BufNewFile,BufRead */.aptitude/config setf aptconf -au BufNewFile,BufRead */etc/apt/apt.conf.d/{[-_[:alnum:]]\+,[-_.[:alnum:]]\+.conf} setf aptconf +" more generic pattern far down " Arch Inventory file au BufNewFile,BufRead .arch-inventory,=tagging-method setf arch @@ -256,7 +256,7 @@ au BufNewFile,BufRead *.lpc,*.ulpc setf lpc au BufNewFile,BufRead calendar setf calendar " C# -au BufNewFile,BufRead *.cs setf cs +au BufNewFile,BufRead *.cs,*.csx setf cs " CSDL au BufNewFile,BufRead *.csdl setf csdl @@ -484,7 +484,10 @@ au BufNewFile,BufRead *.desktop,*.directory setf desktop au BufNewFile,BufRead dict.conf,.dictrc setf dictconf " Dictd config -au BufNewFile,BufRead dictd.conf setf dictdconf +au BufNewFile,BufRead dictd*.conf setf dictdconf + +" DEP3 formatted patch files +au BufNewFile,BufRead */debian/patches/* call dist#ft#Dep3patch() " Diff files au BufNewFile,BufRead *.diff,*.rej setf diff @@ -613,6 +616,9 @@ autocmd BufRead,BufNewFile *.fnl setf fennel " Fetchmail RC file au BufNewFile,BufRead .fetchmailrc setf fetchmail +" Fish shell +au BufNewFile,BufRead *.fish setf fish + " FlexWiki - disabled, because it has side effects when a .wiki file " is not actually FlexWiki "au BufNewFile,BufRead *.wiki setf flexwiki @@ -625,7 +631,7 @@ au BufNewFile,BufRead auto.master setf conf au BufNewFile,BufRead *.mas,*.master setf master " Forth -au BufNewFile,BufRead *.fs,*.ft,*.fth setf forth +au BufNewFile,BufRead *.ft,*.fth setf forth " Reva Forth au BufNewFile,BufRead *.frt setf reva @@ -642,8 +648,14 @@ au BufNewFile,BufRead *.fsl setf framescript " FStab au BufNewFile,BufRead fstab,mtab setf fstab +" F# or Forth +au BufNewFile,BufRead *.fs call dist#ft#FTfs() + +" F# +au BufNewFile,BufRead *.fsi,*.fsx setf fsharp + " GDB command files -au BufNewFile,BufRead .gdbinit setf gdb +au BufNewFile,BufRead .gdbinit,gdbinit setf gdb " GDMO au BufNewFile,BufRead *.mo,*.gdmo setf gdmo @@ -777,6 +789,10 @@ au BufNewFile,BufRead *.hb setf hb " Httest au BufNewFile,BufRead *.htt,*.htb setf httest +" i3 (and sway) +au BufNewFile,BufRead */i3/config,*/sway/config setf i3config +au BufNewFile,BufRead */.i3/config,*/.sway/config setf i3config + " Icon au BufNewFile,BufRead *.icn setf icon @@ -1092,6 +1108,9 @@ au BufNewFile,BufRead *.moo setf moo " Modconf au BufNewFile,BufRead */etc/modules.conf,*/etc/modules,*/etc/conf.modules setf modconf +" MPD is based on XML +au BufNewFile,BufRead *.mpd setf xml + " Mplayer config au BufNewFile,BufRead mplayer.conf,*/.mplayer/config setf mplayerconf @@ -1107,14 +1126,15 @@ au BufNewFile,BufRead *.msql setf msql " Mysql au BufNewFile,BufRead *.mysql setf mysql -" Mutt setup files (must be before catch *.rc) -au BufNewFile,BufRead */etc/Muttrc.d/* call s:StarSetf('muttrc') - " Tcl Shell RC file au BufNewFile,BufRead tclsh.rc setf tcl " M$ Resource files -au BufNewFile,BufRead *.rc,*.rch setf rc +" /etc/Muttrc.d/file.rc is muttrc +au BufNewFile,BufRead *.rc,*.rch + \ if expand("<afile>") !~ "/etc/Muttrc.d/" | + \ setf rc | + \ endif " MuPAD source au BufRead,BufNewFile *.mu setf mupad @@ -1486,6 +1506,9 @@ au BufNewFile,BufRead robots.txt setf robots " Rpcgen au BufNewFile,BufRead *.x setf rpcgen +" MikroTik RouterOS script +au BufRead,BufNewFile *.rsc setf routeros + " reStructuredText Documentation Format au BufNewFile,BufRead *.rst setf rst @@ -1630,6 +1653,9 @@ au BufNewFile,BufRead .tcshrc,*.tcsh,tcsh.tcshrc,tcsh.login call dist#ft#SetFile " (patterns ending in a start further below) au BufNewFile,BufRead .login,.cshrc,csh.cshrc,csh.login,csh.logout,*.csh,.alias call dist#ft#CSH() +" Zig +au BufNewFile,BufRead *.zig setf zig + " Z-Shell script (patterns ending in a star further below) au BufNewFile,BufRead .zprofile,*/etc/zprofile,.zfbfmarks setf zsh au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh @@ -1716,6 +1742,9 @@ au BufNewFile,BufRead *.speedup,*.spdata,*.spd setf spup " Slice au BufNewFile,BufRead *.ice setf slice +" Microsoft Visual Studio Solution +au BufNewFile,BufRead *.sln setf solution + " Spice au BufNewFile,BufRead *.sp,*.spice setf spice @@ -1737,6 +1766,9 @@ au BufNewFile,BufRead *.sqlj setf sqlj " SQR au BufNewFile,BufRead *.sqr,*.sqi setf sqr +" Squirrel +au BufNewFile,BufRead *.nut setf squirrel + " OpenSSH configuration au BufNewFile,BufRead ssh_config,*/.ssh/config setf sshconfig au BufNewFile,BufRead */etc/ssh/ssh_config.d/*.conf setf sshconfig @@ -2049,9 +2081,15 @@ au BufNewFile,BufRead *.xml call dist#ft#FTxml() " XMI (holding UML models) is also XML au BufNewFile,BufRead *.xmi setf xml -" CSPROJ files are Visual Studio.NET's XML-based project config files +" CSPROJ files are Visual Studio.NET's XML-based C# project config files au BufNewFile,BufRead *.csproj,*.csproj.user setf xml +" FSPROJ files are Visual Studio.NET's XML-based F# project config files +au BufNewFile,BufRead *.fsproj,*.fsproj.user setf xml + +" VBPROJ files are Visual Studio.NET's XML-based Visual Basic project config files +au BufNewFile,BufRead *.vbproj,*.vbproj.user setf xml + " Qt Linguist translation source and Qt User Interface Files are XML " However, for .ts Typescript is more common. au BufNewFile,BufRead *.ui setf xml @@ -2137,6 +2175,12 @@ au BufNewFile,BufRead * au StdinReadPost * if !did_filetype() | runtime! scripts.vim | endif +" Plain text files, needs to be far down to not override others. This avoids +" the "conf" type being used if there is a line starting with '#'. +" But before patterns matching everything in a directory. +au BufNewFile,BufRead *.text,README,LICENSE,COPYING,AUTHORS setf text + + " Extra checks for when no filetype has been detected now. Mostly used for " patterns that end in "*". E.g., "zsh*" matches "zsh.vim", but that's a Vim " script file. @@ -2149,7 +2193,10 @@ au BufNewFile,BufRead proftpd.conf* call s:StarSetf('apachestyle') " More Apache config files au BufNewFile,BufRead access.conf*,apache.conf*,apache2.conf*,httpd.conf*,srm.conf* call s:StarSetf('apache') -au BufNewFile,BufRead */etc/apache2/*.conf*,*/etc/apache2/conf.*/*,*/etc/apache2/mods-*/*,*/etc/apache2/sites-*/*,*/etc/httpd/conf.d/*.conf* call s:StarSetf('apache') +au BufNewFile,BufRead */etc/apache2/*.conf*,*/etc/apache2/conf.*/*,*/etc/apache2/mods-*/*,*/etc/apache2/sites-*/*,*/etc/httpd/conf.*/*,*/etc/httpd/mods-*/*,*/etc/httpd/sites-*/*,*/etc/httpd/conf.d/*.conf* call s:StarSetf('apache') + +" APT config file +au BufNewFile,BufRead */etc/apt/apt.conf.d/{[-_[:alnum:]]\+,[-_.[:alnum:]]\+.conf} call s:StarSetf('aptconf') " Asterisk config file au BufNewFile,BufRead *asterisk/*.conf* call s:StarSetf('asterisk') @@ -2252,6 +2299,9 @@ au BufNewFile,BufRead */etc/modutils/* \|endif au BufNewFile,BufRead */etc/modprobe.* call s:StarSetf('modconf') +" Mutt setup files (must be before catch *.rc) +au BufNewFile,BufRead */etc/Muttrc.d/* call s:StarSetf('muttrc') + " Mutt setup file au BufNewFile,BufRead .mutt{ng,}rc*,*/.mutt{ng,}/mutt{ng,}rc* call s:StarSetf('muttrc') au BufNewFile,BufRead mutt{ng,}rc*,Mutt{ng,}rc* call s:StarSetf('muttrc') @@ -2297,6 +2347,9 @@ au BufNewFile,BufRead .bashrc*,.bash[_-]profile*,.bash[_-]logout*,.bash[_-]alias au BufNewFile,BufRead .kshrc* call dist#ft#SetFileTypeSH("ksh") au BufNewFile,BufRead .profile* call dist#ft#SetFileTypeSH(getline(1)) +" Sudoers +au BufNewFile,BufRead */etc/sudoers.d/* call s:StarSetf('sudoers') + " tcsh scripts ending in a star au BufNewFile,BufRead .tcshrc* call dist#ft#SetFileTypeShell("tcsh") @@ -2341,10 +2394,6 @@ au BufNewFile,BufRead .zsh*,.zlog*,.zcompdump* call s:StarSetf('zsh') au BufNewFile,BufRead zsh*,zlog* call s:StarSetf('zsh') -" Plain text files, needs to be far down to not override others. This avoids -" the "conf" type being used if there is a line starting with '#'. -au BufNewFile,BufRead *.text,README setf text - " Help files match *.txt but should have a last line that is a modeline. au BufNewFile,BufRead *.txt \ if getline('$') !~ 'vim:.*ft=help' diff --git a/runtime/ftplugin/aap.vim b/runtime/ftplugin/aap.vim index 9b20ec4766..b5065e5157 100644 --- a/runtime/ftplugin/aap.vim +++ b/runtime/ftplugin/aap.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: Aap recipe " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2013 Apr 05 +" Last Change: 2021 Nov 14 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -11,8 +11,9 @@ endif " Don't load another plugin for this buffer let b:did_ftplugin = 1 -" Reset 'formatoptions', 'comments' and 'expandtab' to undo this plugin. -let b:undo_ftplugin = "setl fo< com< et<" +" Reset 'formatoptions', 'comments', 'commentstring' and 'expandtab' to undo +" this plugin. +let b:undo_ftplugin = "setl fo< com< cms< et<" " Set 'formatoptions' to break comment lines but not other lines, " and insert the comment leader when hitting <CR> or using "o". @@ -20,6 +21,12 @@ setlocal fo-=t fo+=croql " Set 'comments' to format dashed lists in comments. setlocal comments=s:#\ -,m:#\ \ ,e:#,n:#,fb:- +setlocal commentstring=#\ %s " Expand tabs to spaces to avoid trouble. setlocal expandtab + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Aap Recipe Files (*.aap)\t*.aap\nAll Files (*.*)\t*.*\n" + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif diff --git a/runtime/ftplugin/checkhealth.vim b/runtime/ftplugin/checkhealth.vim new file mode 100644 index 0000000000..3d8e9ace1a --- /dev/null +++ b/runtime/ftplugin/checkhealth.vim @@ -0,0 +1,20 @@ +" Vim filetype plugin +" Language: Neovim checkhealth buffer +" Last Change: 2021 Dec 15 + +if exists("b:did_ftplugin") + finish +endif + +runtime! ftplugin/markdown.vim ftplugin/markdown_*.vim ftplugin/markdown/*.vim + +setlocal wrap breakindent linebreak +setlocal conceallevel=2 concealcursor=nc +setlocal keywordprg=:help +let &l:iskeyword='!-~,^*,^|,^",192-255' + +if exists("b:undo_ftplugin") + let b:undo_ftplugin .= "|setl wrap< bri< lbr< cole< cocu< kp< isk<" +else + let b:undo_ftplugin = "setl wrap< bri< lbr< cole< cocu< kp< isk<" +endif diff --git a/runtime/ftplugin/diff.vim b/runtime/ftplugin/diff.vim index 0464290475..bf37d464c0 100644 --- a/runtime/ftplugin/diff.vim +++ b/runtime/ftplugin/diff.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: Diff " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2020 Jul 18 +" Last Change: 2021 Nov 14 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -9,10 +9,15 @@ if exists("b:did_ftplugin") endif let b:did_ftplugin = 1 -let b:undo_ftplugin = "setl modeline<" +let b:undo_ftplugin = "setl modeline< commentstring<" " Don't use modelines in a diff, they apply to the diffed file setlocal nomodeline " If there are comments they start with # -let &commentstring = "# %s" +let &l:commentstring = "# %s" + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Diff Files (*.diff)\t*.diff\nPatch Files (*.patch)\t*.h\nAll Files (*.*)\t*.*\n" + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif diff --git a/runtime/ftplugin/i3config.vim b/runtime/ftplugin/i3config.vim new file mode 100644 index 0000000000..4204510e64 --- /dev/null +++ b/runtime/ftplugin/i3config.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin file +" Language: i3 config file +" Original Author: Mohamed Boughaba <mohamed dot bgb at gmail dot com> +" Maintainer: Quentin Hibon +" Version: 0.4 +" Last Change: 2021 Dec 14 + +if exists("b:did_ftplugin") | finish | endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setlocal cms<" + +setlocal commentstring=#\ %s diff --git a/runtime/ftplugin/indent.vim b/runtime/ftplugin/indent.vim index e6d928a073..64a650ad7b 100644 --- a/runtime/ftplugin/indent.vim +++ b/runtime/ftplugin/indent.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file -" Language: indent(1) configuration file -" Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2008-07-09 +" Language: indent(1) configuration file +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2008-07-09 if exists("b:did_ftplugin") finish diff --git a/runtime/ftplugin/jsonc.vim b/runtime/ftplugin/jsonc.vim index 90d52cd0d3..e47a75f574 100644 --- a/runtime/ftplugin/jsonc.vim +++ b/runtime/ftplugin/jsonc.vim @@ -4,7 +4,7 @@ " Acknowledgement: Based off of vim-jsonc maintained by Kevin Locke <kevin@kevinlocke.name> " https://github.com/kevinoid/vim-jsonc " License: MIT -" Last Change: 2021-07-01 +" Last Change: 2021 Nov 22 runtime! ftplugin/json.vim @@ -14,14 +14,8 @@ else let b:did_ftplugin_jsonc = 1 endif -" A list of commands that undo buffer local changes made below. -let s:undo_ftplugin = [] - " Set comment (formatting) related options. {{{1 setlocal commentstring=//%s comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// -call add(s:undo_ftplugin, 'commentstring< comments<') " Let Vim know how to disable the plug-in. -call map(s:undo_ftplugin, "'execute ' . string(v:val)") -let b:undo_ftplugin = join(s:undo_ftplugin, ' | ') -unlet s:undo_ftplugin +let b:undo_ftplugin = 'setlocal commentstring< comments<' diff --git a/runtime/ftplugin/lua.vim b/runtime/ftplugin/lua.vim index 3454a4d694..2604257594 100644 --- a/runtime/ftplugin/lua.vim +++ b/runtime/ftplugin/lua.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file. -" Language: Lua 4.0+ -" Maintainer: Max Ischenko <mfi@ukr.net> -" Last Change: 2012 Mar 07 +" Language: Lua +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Max Ischenko <mfi@ukr.net> +" Last Change: 2021 Nov 15 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -16,27 +17,30 @@ set cpo&vim " Set 'formatoptions' to break comment lines but not other lines, and insert " the comment leader when hitting <CR> or using "o". -setlocal fo-=t fo+=croql +setlocal formatoptions-=t formatoptions+=croql -setlocal com=:-- -setlocal cms=--%s +setlocal comments=:-- +setlocal commentstring=--%s setlocal suffixesadd=.lua +let b:undo_ftplugin = "setlocal fo< com< cms< sua<" -" The following lines enable the macros/matchit.vim plugin for -" extended matching with the % key. -if exists("loaded_matchit") - +if exists("loaded_matchit") && !exists("b:match_words") let b:match_ignorecase = 0 let b:match_words = - \ '\<\%(do\|function\|if\)\>:' . - \ '\<\%(return\|else\|elseif\)\>:' . - \ '\<end\>,' . - \ '\<repeat\>:\<until\>' + \ '\<\%(do\|function\|if\)\>:' . + \ '\<\%(return\|else\|elseif\)\>:' . + \ '\<end\>,' . + \ '\<repeat\>:\<until\>,' . + \ '\%(--\)\=\[\(=*\)\[:]\1]' + let b:undo_ftplugin .= " | unlet! b:match_words b:match_ignorecase" +endif -endif " exists("loaded_matchit") +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Lua Source Files (*.lua)\t*.lua\n" . + \ "All Files (*.*)\t*.*\n" + let b:undo_ftplugin .= " | unlet! b:browsefilter" +endif let &cpo = s:cpo_save unlet s:cpo_save - -let b:undo_ftplugin = "setlocal fo< com< cms< suffixesadd<" diff --git a/runtime/ftplugin/routeros.vim b/runtime/ftplugin/routeros.vim new file mode 100644 index 0000000000..c6e4799aa2 --- /dev/null +++ b/runtime/ftplugin/routeros.vim @@ -0,0 +1,29 @@ +" Vim filetype plugin file +" Language: MikroTik RouterOS Script +" Maintainer: zainin <z@wintr.dev> +" Last Change: 2021 Nov 14 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:save_cpo = &cpo +set cpo-=C + +setlocal comments=:# +setlocal commentstring=#\ %s +setlocal formatoptions-=t formatoptions+=croql + +let b:undo_ftplugin = "setlocal com< cms< fo<" + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "RouterOS Script Files (*.rsc)\t*.rsc\n" .. + \ "All Files (*.*)\t*.*\n" + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif + +let &cpo = s:save_cpo +unlet! s:save_cpo + +" vim: nowrap sw=2 sts=2 ts=8 noet: diff --git a/runtime/ftplugin/solution.vim b/runtime/ftplugin/solution.vim new file mode 100644 index 0000000000..bd30c7bb19 --- /dev/null +++ b/runtime/ftplugin/solution.vim @@ -0,0 +1,37 @@ +" Vim filetype plugin file +" Language: Microsoft Visual Studio Solution +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2021 Dec 15 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpo +set cpo&vim + +setlocal comments=:# +setlocal commentstring=#\ %s + +let b:undo_ftplugin = "setl com< cms<" + +if exists("loaded_matchit") && !exists("b:match_words") + let b:match_words = + \ '\<Project\>:\<EndProject\>,' .. + \ '\<ProjectSection\>:\<EndProjectSection\>,' .. + \ '\<Global\>:\<EndGlobal\>,' .. + \ '\<GlobalSection\>:\<EndGlobalSection\>' + let b:undo_ftplugin ..= " | unlet! b:match_words" +endif + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Microsoft Visual Studio Solution Files\t*.sln\n" .. + \ "All Files (*.*)\t*.*\n" + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: nowrap sw=2 sts=2 ts=8 noet: diff --git a/runtime/ftplugin/vb.vim b/runtime/ftplugin/vb.vim index d70db89273..5a9548115b 100644 --- a/runtime/ftplugin/vb.vim +++ b/runtime/ftplugin/vb.vim @@ -1,44 +1,70 @@ " Vim filetype plugin file -" Language: VisualBasic (ft=vb) -" Maintainer: Johannes Zellner <johannes@zellner.org> -" Last Change: Thu, 22 Nov 2001 12:56:14 W. Europe Standard Time +" Language: Visual Basic (ft=vb) +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Johannes Zellner <johannes@zellner.org> +" Last Change: 2021 Nov 17 -if exists("b:did_ftplugin") | finish | endif +if exists("b:did_ftplugin") + finish +endif let b:did_ftplugin = 1 -setlocal com=sr:'\ -,mb:'\ \ ,el:'\ \ ,:' +let s:cpo_save = &cpo +set cpo&vim + +setlocal comments=sr:'\ -,mb:'\ \ ,el:'\ \ ,:' +setlocal commentstring='\ %s +setlocal formatoptions-=t formatoptions+=croql + +let b:undo_ftplugin = "setlocal com< cms< fo<" " we need this wrapper, as call doesn't allow a count -fun! <SID>VbSearch(pattern, flags) +function! s:VbSearch(pattern, flags) let cnt = v:count1 while cnt > 0 call search(a:pattern, a:flags) let cnt = cnt - 1 endwhile -endfun +endfunction -let s:cpo_save = &cpo -set cpo&vim +if !exists("no_plugin_maps") && !exists("no_vb_maps") + nnoremap <buffer> <silent> [[ <Cmd>call <SID>VbSearch('^\s*\%(\%(private\<Bar>public\)\s\+\)\=\%(function\<Bar>sub\)', 'sbW')<CR> + vnoremap <buffer> <silent> [[ <Cmd>call <SID>VbSearch('^\s*\%(\%(private\<Bar>public\)\s\+\)\=\%(function\<Bar>sub\)', 'sbW')<CR> + nnoremap <buffer> <silent> ]] <Cmd>call <SID>VbSearch('^\s*\%(\%(private\<Bar>public\)\s\+\)\=\%(function\<Bar>sub\)', 'sW')<CR> + vnoremap <buffer> <silent> ]] <Cmd>call <SID>VbSearch('^\s*\%(\%(private\<Bar>public\)\s\+\)\=\%(function\<Bar>sub\)', 'sW')<CR> + nnoremap <buffer> <silent> [] <Cmd>call <SID>VbSearch('^\s*end\s\+\%(function\<Bar>sub\)', 'sbW')<CR> + vnoremap <buffer> <silent> [] <Cmd>call <SID>VbSearch('^\s*end\s\+\%(function\<Bar>sub\)', 'sbW')<CR> + nnoremap <buffer> <silent> ][ <Cmd>call <SID>VbSearch('^\s*end\s\+\%(function\<Bar>sub\)', 'sW')<CR> + vnoremap <buffer> <silent> ][ <Cmd>call <SID>VbSearch('^\s*end\s\+\%(function\<Bar>sub\)', 'sW')<CR> + let b:undo_ftplugin .= " | sil! exe 'nunmap <buffer> [[' | sil! exe 'vunmap <buffer> [['" . + \ " | sil! exe 'nunmap <buffer> ]]' | sil! exe 'vunmap <buffer> ]]'" . + \ " | sil! exe 'nunmap <buffer> []' | sil! exe 'vunmap <buffer> []'" . + \ " | sil! exe 'nunmap <buffer> ][' | sil! exe 'vunmap <buffer> ]['" +endif + +" TODO: line start anchors are almost certainly overly restrictive - allow +" after statement separators. Even in QuickBasic only block IF statements +" were required to be at the start of a line. +if exists("loaded_matchit") && !exists("b:match_words") + let b:match_ignorecase = 1 + let b:match_words = + \ '\%(^\s*\)\@<=\<if\>.*\<then\>\s*\%($\|''\):\%(^\s*\)\@<=\<else\>:\%(^\s*\)\@<=\<elseif\>:\%(^\s*\)\@<=\<end\>\s\+\<if\>,' . + \ '\%(^\s*\)\@<=\<for\>:\%(^\s*\)\@<=\<next\>,' . + \ '\%(^\s*\)\@<=\<while\>:\%(^\s*\)\@<=\<wend\>,' . + \ '\%(^\s*\)\@<=\<do\>:\%(^\s*\)\@<=\<loop\>\s\+\<while\>,' . + \ '\%(^\s*\)\@<=\<select\>\s\+\<case\>:\%(^\s*\)\@<=\<case\>:\%(^\s*\)\@<=\<end\>\s\+\<select\>,' . + \ '\%(^\s*\)\@<=\<enum\>:\%(^\s*\)\@<=\<end\>\s\<enum\>,' . + \ '\%(^\s*\)\@<=\<with\>:\%(^\s*\)\@<=\<end\>\s\<with\>,' . + \ '\%(^\s*\)\@<=\%(\<\%(private\|public\)\>\s\+\)\=\<function\>\s\+\([^ \t(]\+\):\%(^\s*\)\@<=\<\1\>\s*=:\%(^\s*\)\@<=\<end\>\s\+\<function\>,' . + \ '\%(^\s*\)\@<=\%(\<\%(private\|public\)\>\s\+\)\=\<sub\>\s\+:\%(^\s*\)\@<=\<end\>\s\+\<sub\>' + let b:undo_ftplugin .= " | unlet! b:match_words b:match_ignorecase" +endif -" NOTE the double escaping \\| -nnoremap <buffer> <silent> [[ :call <SID>VbSearch('^\s*\(\(private\|public\)\s\+\)\=\(function\\|sub\)', 'bW')<cr> -nnoremap <buffer> <silent> ]] :call <SID>VbSearch('^\s*\(\(private\|public\)\s\+\)\=\(function\\|sub\)', 'W')<cr> -nnoremap <buffer> <silent> [] :call <SID>VbSearch('^\s*\<end\>\s\+\(function\\|sub\)', 'bW')<cr> -nnoremap <buffer> <silent> ][ :call <SID>VbSearch('^\s*\<end\>\s\+\(function\\|sub\)', 'W')<cr> - -" matchit support -if exists("loaded_matchit") - let b:match_ignorecase=1 - let b:match_words= - \ '\%(^\s*\)\@<=\<if\>.*\<then\>\s*$:\%(^\s*\)\@<=\<else\>:\%(^\s*\)\@<=\<elseif\>:\%(^\s*\)\@<=\<end\>\s\+\<if\>,' . - \ '\%(^\s*\)\@<=\<for\>:\%(^\s*\)\@<=\<next\>,' . - \ '\%(^\s*\)\@<=\<while\>:\%(^\s*\)\@<=\<wend\>,' . - \ '\%(^\s*\)\@<=\<do\>:\%(^\s*\)\@<=\<loop\>\s\+\<while\>,' . - \ '\%(^\s*\)\@<=\<select\>\s\+\<case\>:\%(^\s*\)\@<=\<case\>:\%(^\s*\)\@<=\<end\>\s\+\<select\>,' . - \ '\%(^\s*\)\@<=\<enum\>:\%(^\s*\)\@<=\<end\>\s\<enum\>,' . - \ '\%(^\s*\)\@<=\<with\>:\%(^\s*\)\@<=\<end\>\s\<with\>,' . - \ '\%(^\s*\)\@<=\%(\<\%(private\|public\)\>\s\+\)\=\<function\>\s\+\([^ \t(]\+\):\%(^\s*\)\@<=\<\1\>\s*=:\%(^\s*\)\@<=\<end\>\s\+\<function\>,' . - \ '\%(^\s*\)\@<=\%(\<\%(private\|public\)\>\s\+\)\=\<sub\>\s\+:\%(^\s*\)\@<=\<end\>\s\+\<sub\>' +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Visual Basic Source Files (*.bas)\t*.bas\n" . + \ "Visual Basic Form Files (*.frm)\t*.frm\n" . + \ "All Files (*.*)\t*.*\n" + let b:undo_ftplugin .= " | unlet! b:browsefilter" endif let &cpo = s:cpo_save diff --git a/runtime/ftplugin/zimbu.vim b/runtime/ftplugin/zimbu.vim index 24674776cb..e365ccf07e 100644 --- a/runtime/ftplugin/zimbu.vim +++ b/runtime/ftplugin/zimbu.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: Zimbu " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2017 Dec 05 +" Last Change: 2021 Nov 12 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -34,9 +34,11 @@ setlocal errorformat^=%f\ line\ %l\ col\ %c:\ %m,ERROR:\ %m " When the matchit plugin is loaded, this makes the % command skip parens and " braces in comments. -let b:match_words = '\(^\s*\)\@<=\(MODULE\|CLASS\|INTERFACE\|BITS\|ENUM\|SHARED\|FUNC\|REPLACE\|DEFINE\|PROC\|EQUAL\|MAIN\|IF\|GENERATE_IF\|WHILE\|REPEAT\|WITH\|DO\|FOR\|SWITCH\|TRY\)\>\|{\s*$:\(^\s*\)\@<=\(ELSE\|ELSEIF\|GENERATE_ELSE\|GENERATE_ELSEIF\|CATCH\|FINALLY\)\>:\(^\s*\)\@<=\(}\|\<UNTIL\>\)' - -let b:match_skip = 's:comment\|string\|zimbuchar' +if exists("loaded_matchit") && !exists("b:match_words") + let b:match_words = '\(^\s*\)\@<=\(MODULE\|CLASS\|INTERFACE\|BITS\|ENUM\|SHARED\|FUNC\|REPLACE\|DEFINE\|PROC\|EQUAL\|MAIN\|IF\|GENERATE_IF\|WHILE\|REPEAT\|WITH\|DO\|FOR\|SWITCH\|TRY\)\>\|{\s*$:\(^\s*\)\@<=\(ELSE\|ELSEIF\|GENERATE_ELSE\|GENERATE_ELSEIF\|CATCH\|FINALLY\)\>:\(^\s*\)\@<=\(}\|\<UNTIL\>\)' + let b:match_skip = 's:comment\|string\|zimbuchar' + let b:undo_ftplugin ..= " | unlet! b:match_words b:match_skip" +endif setlocal tw=78 setlocal et sts=2 sw=2 @@ -135,9 +137,60 @@ iabbr <buffer> <expr> until GCUpperSpace("until") iabbr <buffer> <expr> while GCUpperSpace("while") iabbr <buffer> <expr> repeat GCUpper("repeat") +let b:undo_ftplugin ..= + \ " | iunabbr <buffer> alias" .. + \ " | iunabbr <buffer> arg" .. + \ " | iunabbr <buffer> break" .. + \ " | iunabbr <buffer> case" .. + \ " | iunabbr <buffer> catch" .. + \ " | iunabbr <buffer> check" .. + \ " | iunabbr <buffer> class" .. + \ " | iunabbr <buffer> interface" .. + \ " | iunabbr <buffer> implements" .. + \ " | iunabbr <buffer> shared" .. + \ " | iunabbr <buffer> continue" .. + \ " | iunabbr <buffer> default" .. + \ " | iunabbr <buffer> extends" .. + \ " | iunabbr <buffer> do" .. + \ " | iunabbr <buffer> else" .. + \ " | iunabbr <buffer> elseif" .. + \ " | iunabbr <buffer> enum" .. + \ " | iunabbr <buffer> exit" .. + \ " | iunabbr <buffer> false" .. + \ " | iunabbr <buffer> fail" .. + \ " | iunabbr <buffer> finally" .. + \ " | iunabbr <buffer> for" .. + \ " | iunabbr <buffer> func" .. + \ " | iunabbr <buffer> if" .. + \ " | iunabbr <buffer> import" .. + \ " | iunabbr <buffer> in" .. + \ " | iunabbr <buffer> io" .. + \ " | iunabbr <buffer> main" .. + \ " | iunabbr <buffer> module" .. + \ " | iunabbr <buffer> new" .. + \ " | iunabbr <buffer> nil" .. + \ " | iunabbr <buffer> ok" .. + \ " | iunabbr <buffer> proc" .. + \ " | iunabbr <buffer> proceed" .. + \ " | iunabbr <buffer> return" .. + \ " | iunabbr <buffer> step" .. + \ " | iunabbr <buffer> switch" .. + \ " | iunabbr <buffer> sys" .. + \ " | iunabbr <buffer> this" .. + \ " | iunabbr <buffer> throw" .. + \ " | iunabbr <buffer> try" .. + \ " | iunabbr <buffer> to" .. + \ " | iunabbr <buffer> true" .. + \ " | iunabbr <buffer> until" .. + \ " | iunabbr <buffer> while" .. + \ " | iunabbr <buffer> repeat" + if !exists("no_plugin_maps") && !exists("no_zimbu_maps") nnoremap <silent> <buffer> [[ m`:call ZimbuGoStartBlock()<CR> nnoremap <silent> <buffer> ]] m`:call ZimbuGoEndBlock()<CR> + let b:undo_ftplugin ..= + \ " | silent! exe 'nunmap <buffer> [['" .. + \ " | silent! exe 'nunmap <buffer> ]]'" endif " Using a function makes sure the search pattern is restored diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim index a98c75e541..7c03ff2873 100644 --- a/runtime/indent/vim.vim +++ b/runtime/indent/vim.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: Vim script " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Nov 03 +" Last Change: 2021 Nov 27 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -12,6 +12,7 @@ let b:did_indent = 1 setlocal indentexpr=GetVimIndent() setlocal indentkeys+==end,=},=else,=cat,=finall,=END,0\\,0=\"\\\ setlocal indentkeys-=0# +setlocal indentkeys-=: let b:undo_indent = "setl indentkeys< indentexpr<" diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua index 1a258546a5..9327c652db 100644 --- a/runtime/lua/vim/F.lua +++ b/runtime/lua/vim/F.lua @@ -27,7 +27,7 @@ function F.nil_wrap(fn) end end ---- like {...} except preserve the lenght explicitly +--- like {...} except preserve the length explicitly function F.pack_len(...) return {n=select('#', ...), ...} end diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index f7d47c1030..522e26caa7 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -16,10 +16,6 @@ for _, v in pairs(a.nvim_get_all_options_info()) do if v.shortname ~= "" then options_info[v.shortname] = v end end -local is_global_option = function(info) return info.scope == "global" end -local is_buffer_option = function(info) return info.scope == "buf" end -local is_window_option = function(info) return info.scope == "win" end - local get_scoped_options = function(scope) local result = {} for name, option_info in pairs(options_info) do @@ -133,107 +129,18 @@ do -- window option accessor vim.wo = new_win_opt_accessor(nil) end ---[[ -Local window setter - -buffer options: does not get copied when split - nvim_set_option(buf_opt, value) -> sets the default for NEW buffers - this sets the hidden global default for buffer options - - nvim_buf_set_option(...) -> sets the local value for the buffer - - set opt=value, does BOTH global default AND buffer local value - setlocal opt=value, does ONLY buffer local value - -window options: gets copied - does not need to call nvim_set_option because nobody knows what the heck this does⸮ - We call it anyway for more readable code. - - - Command global value local value - :set option=value set set - :setlocal option=value - set -:setglobal option=value set - ---]] -local function set_scoped_option(k, v, set_type) - local info = options_info[k] - - -- Don't let people do setlocal with global options. - -- That is a feature that doesn't make sense. - if set_type == SET_TYPES.LOCAL and is_global_option(info) then - error(string.format("Unable to setlocal option: '%s', which is a global option.", k)) - end - - -- Only `setlocal` skips setting the default/global value - -- This will more-or-less noop for window options, but that's OK - if set_type ~= SET_TYPES.LOCAL then - a.nvim_set_option(k, v) - end - - if is_window_option(info) then - if set_type ~= SET_TYPES.GLOBAL then - a.nvim_win_set_option(0, k, v) - end - elseif is_buffer_option(info) then - if set_type == SET_TYPES.LOCAL - or (set_type == SET_TYPES.SET and not info.global_local) then - a.nvim_buf_set_option(0, k, v) - end - end -end - ---[[ -Local window getter - - Command global value local value - :set option? - display - :setlocal option? - display -:setglobal option? display - ---]] -local function get_scoped_option(k, set_type) - local info = assert(options_info[k], "Must be a valid option: " .. tostring(k)) - - if set_type == SET_TYPES.GLOBAL or is_global_option(info) then - return a.nvim_get_option(k) - end - - if is_buffer_option(info) then - local was_set, value = pcall(a.nvim_buf_get_option, 0, k) - if was_set then return value end - - if info.global_local then - return a.nvim_get_option(k) - end - - error("buf_get: This should not be able to happen, given my understanding of options // " .. k) - end - - if is_window_option(info) then - local ok, value = pcall(a.nvim_win_get_option, 0, k) - if ok then - return value - end - - local global_ok, global_val = pcall(a.nvim_get_option, k) - if global_ok then - return global_val - end - - error("win_get: This should never happen. File an issue and tag @tjdevries") - end - - error("This fallback case should not be possible. " .. k) -end - -- vim global option -- this ONLY sets the global option. like `setglobal` -vim.go = make_meta_accessor(a.nvim_get_option, a.nvim_set_option) +vim.go = make_meta_accessor( + function(k) return a.nvim_get_option_value(k, {scope = "global"}) end, + function(k, v) return a.nvim_set_option_value(k, v, {scope = "global"}) end +) -- vim `set` style options. -- it has no additional metamethod magic. vim.o = make_meta_accessor( - function(k) return get_scoped_option(k, SET_TYPES.SET) end, - function(k, v) return set_scoped_option(k, v, SET_TYPES.SET) end + function(k) return a.nvim_get_option_value(k, {}) end, + function(k, v) return a.nvim_set_option_value(k, v, {}) end ) ---@brief [[ @@ -389,6 +296,10 @@ local convert_value_to_vim = (function() } return function(name, info, value) + if value == nil then + return vim.NIL + end + local option_type = get_option_type(name, info) assert_valid_value(name, value, valid_types[option_type]) @@ -398,7 +309,7 @@ end)() --- Converts a vimoption_T style value to a Lua value local convert_value_to_lua = (function() - -- Map of OptionType to functions that take vimoption_T values and conver to lua values. + -- Map of OptionType to functions that take vimoption_T values and convert to lua values. -- Each function takes (info, vim_value) -> lua_value local to_lua_value = { [OptionTypes.BOOLEAN] = function(_, value) return value end, @@ -671,15 +582,19 @@ local create_option_metatable = function(set_type) }, option_mt) end - -- TODO(tjdevries): consider supporting `nil` for set to remove the local option. - -- vim.cmd [[set option<]] + local scope + if set_type == SET_TYPES.GLOBAL then + scope = "global" + elseif set_type == SET_TYPES.LOCAL then + scope = "local" + end option_mt = { -- To set a value, instead use: -- opt[my_option] = value _set = function(self) local value = convert_value_to_vim(self._name, self._info, self._value) - set_scoped_option(self._name, value, set_type) + a.nvim_set_option_value(self._name, value, {scope = scope}) return self end, @@ -716,7 +631,7 @@ local create_option_metatable = function(set_type) set_mt = { __index = function(_, k) - return make_option(k, get_scoped_option(k, set_type)) + return make_option(k, a.nvim_get_option_value(k, {scope = scope})) end, __newindex = function(_, k, v) diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index b30a678eeb..742ebf69b2 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -36,7 +36,34 @@ M.handlers = setmetatable({}, { end, }) --- Local functions {{{ +-- Metatable that automatically creates an empty table when assigning to a missing key +local bufnr_and_namespace_cacher_mt = { + __index = function(t, bufnr) + assert(bufnr > 0, "Invalid buffer number") + t[bufnr] = {} + return t[bufnr] + end, +} + +local diagnostic_cache = setmetatable({}, { + __index = function(t, bufnr) + assert(bufnr > 0, "Invalid buffer number") + vim.api.nvim_buf_attach(bufnr, false, { + on_detach = function() + rawset(t, bufnr, nil) -- clear cache + end + }) + t[bufnr] = {} + return t[bufnr] + end, +}) + +local diagnostic_cache_extmarks = setmetatable({}, bufnr_and_namespace_cacher_mt) +local diagnostic_attached_buffers = {} +local diagnostic_disabled = {} +local bufs_waiting_to_update = setmetatable({}, bufnr_and_namespace_cacher_mt) + +local all_namespaces = {} ---@private local function to_severity(severity) @@ -64,23 +91,22 @@ local function filter_by_severity(severity, diagnostics) end ---@private -local function prefix_source(source, diagnostics) - vim.validate { source = {source, function(v) - return v == "always" or v == "if_many" - end, "'always' or 'if_many'" } } - - if source == "if_many" then - local sources = {} - for _, d in pairs(diagnostics) do - if d.source then - sources[d.source] = true +local function count_sources(bufnr) + local seen = {} + local count = 0 + for _, namespace_diagnostics in pairs(diagnostic_cache[bufnr]) do + for _, diagnostic in ipairs(namespace_diagnostics) do + if diagnostic.source and not seen[diagnostic.source] then + seen[diagnostic.source] = true + count = count + 1 end end - if #vim.tbl_keys(sources) <= 1 then - return diagnostics - end end + return count +end +---@private +local function prefix_source(diagnostics) return vim.tbl_map(function(d) if not d.source then return d @@ -106,8 +132,6 @@ local function reformat_diagnostics(format, diagnostics) return formatted end -local all_namespaces = {} - ---@private local function enabled_value(option, namespace) local ns = namespace and M.get_namespace(namespace) or {} @@ -213,38 +237,13 @@ local function get_bufnr(bufnr) return bufnr end --- Metatable that automatically creates an empty table when assigning to a missing key -local bufnr_and_namespace_cacher_mt = { - __index = function(t, bufnr) - if not bufnr or bufnr == 0 then - bufnr = vim.api.nvim_get_current_buf() - end - - if rawget(t, bufnr) == nil then - rawset(t, bufnr, {}) - end - - return rawget(t, bufnr) - end, - - __newindex = function(t, bufnr, v) - if not bufnr or bufnr == 0 then - bufnr = vim.api.nvim_get_current_buf() - end - - rawset(t, bufnr, v) - end, -} - -local diagnostic_cleanup = setmetatable({}, bufnr_and_namespace_cacher_mt) -local diagnostic_cache = setmetatable({}, bufnr_and_namespace_cacher_mt) -local diagnostic_cache_extmarks = setmetatable({}, bufnr_and_namespace_cacher_mt) -local diagnostic_attached_buffers = {} -local diagnostic_disabled = {} -local bufs_waiting_to_update = setmetatable({}, bufnr_and_namespace_cacher_mt) - ---@private local function is_disabled(namespace, bufnr) + local ns = M.get_namespace(namespace) + if ns.disabled then + return true + end + if type(diagnostic_disabled[bufnr]) == "table" then return diagnostic_disabled[bufnr][namespace] end @@ -272,6 +271,8 @@ end ---@private local function set_diagnostic_cache(namespace, bufnr, diagnostics) for _, diagnostic in ipairs(diagnostics) do + assert(diagnostic.lnum, "Diagnostic line number is required") + assert(diagnostic.col, "Diagnostic column is required") diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity) or M.severity.ERROR diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum diagnostic.end_col = diagnostic.end_col or diagnostic.col @@ -282,11 +283,6 @@ local function set_diagnostic_cache(namespace, bufnr, diagnostics) end ---@private -local function clear_diagnostic_cache(namespace, bufnr) - diagnostic_cache[bufnr][namespace] = nil -end - ----@private local function restore_extmarks(bufnr, last) for ns, extmarks in pairs(diagnostic_cache_extmarks[bufnr]) do local extmarks_current = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {details = true}) @@ -302,11 +298,6 @@ local function restore_extmarks(bufnr, last) if not found[extmark[1]] then local opts = extmark[4] opts.id = extmark[1] - -- HACK: end_row should be end_line - if opts.end_row then - opts.end_line = opts.end_row - opts.end_row = nil - end pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts) end end @@ -372,6 +363,71 @@ local function clear_scheduled_display(namespace, bufnr) end ---@private +local function get_diagnostics(bufnr, opts, clamp) + opts = opts or {} + + local namespace = opts.namespace + local diagnostics = {} + + -- Memoized results of buf_line_count per bufnr + local buf_line_count = setmetatable({}, { + __index = function(t, k) + t[k] = vim.api.nvim_buf_line_count(k) + return rawget(t, k) + end, + }) + + ---@private + local function add(b, d) + if not opts.lnum or d.lnum == opts.lnum then + if clamp and vim.api.nvim_buf_is_loaded(b) then + local line_count = buf_line_count[b] - 1 + if (d.lnum > line_count or d.end_lnum > line_count or d.lnum < 0 or d.end_lnum < 0) then + d = vim.deepcopy(d) + d.lnum = math.max(math.min(d.lnum, line_count), 0) + d.end_lnum = math.max(math.min(d.end_lnum, line_count), 0) + end + end + table.insert(diagnostics, d) + end + end + + if namespace == nil and bufnr == nil then + for b, t in pairs(diagnostic_cache) do + for _, v in pairs(t) do + for _, diagnostic in pairs(v) do + add(b, diagnostic) + end + end + end + elseif namespace == nil then + bufnr = get_bufnr(bufnr) + for iter_namespace in pairs(diagnostic_cache[bufnr]) do + for _, diagnostic in pairs(diagnostic_cache[bufnr][iter_namespace]) do + add(bufnr, diagnostic) + end + end + elseif bufnr == nil then + for b, t in pairs(diagnostic_cache) do + for _, diagnostic in pairs(t[namespace] or {}) do + add(b, diagnostic) + end + end + else + bufnr = get_bufnr(bufnr) + for _, diagnostic in pairs(diagnostic_cache[bufnr][namespace] or {}) do + add(bufnr, diagnostic) + end + end + + if opts.severity then + diagnostics = filter_by_severity(opts.severity, diagnostics) + end + + return diagnostics +end + +---@private local function set_list(loclist, opts) opts = opts or {} local open = vim.F.if_nil(opts.open, true) @@ -381,7 +437,9 @@ local function set_list(loclist, opts) if loclist then bufnr = vim.api.nvim_win_get_buf(winnr) end - local diagnostics = M.get(bufnr, opts) + -- Don't clamp line numbers since the quickfix list can already handle line + -- numbers beyond the end of the buffer + local diagnostics = get_diagnostics(bufnr, opts, false) local items = M.toqflist(diagnostics) if loclist then vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items }) @@ -394,27 +452,12 @@ local function set_list(loclist, opts) end ---@private ---- To (slightly) improve performance, modifies diagnostics in place. -local function clamp_line_numbers(bufnr, diagnostics) - local buf_line_count = vim.api.nvim_buf_line_count(bufnr) - if buf_line_count == 0 then - return - end - - for _, diagnostic in ipairs(diagnostics) do - diagnostic.lnum = math.max(math.min(diagnostic.lnum, buf_line_count - 1), 0) - diagnostic.end_lnum = math.max(math.min(diagnostic.end_lnum, buf_line_count - 1), 0) - end -end - ----@private local function next_diagnostic(position, search_forward, bufnr, opts, namespace) position[1] = position[1] - 1 bufnr = get_bufnr(bufnr) local wrap = vim.F.if_nil(opts.wrap, true) local line_count = vim.api.nvim_buf_line_count(bufnr) - local diagnostics = M.get(bufnr, vim.tbl_extend("keep", opts, {namespace = namespace})) - clamp_line_numbers(bufnr, diagnostics) + local diagnostics = get_diagnostics(bufnr, vim.tbl_extend("keep", opts, {namespace = namespace}), true) local line_diagnostics = diagnostic_lines(diagnostics) for i = 0, line_count do local offset = i * (search_forward and 1 or -1) @@ -426,13 +469,14 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace) lnum = (lnum + line_count) % line_count end if line_diagnostics[lnum] and not vim.tbl_isempty(line_diagnostics[lnum]) then + local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1] local sort_diagnostics, is_next if search_forward then sort_diagnostics = function(a, b) return a.col < b.col end - is_next = function(diagnostic) return diagnostic.col > position[2] end + is_next = function(d) return math.min(d.col, line_length - 1) > position[2] end else sort_diagnostics = function(a, b) return a.col > b.col end - is_next = function(diagnostic) return diagnostic.col < position[2] end + is_next = function(d) return math.min(d.col, line_length - 1) < position[2] end end table.sort(line_diagnostics[lnum], sort_diagnostics) if i == 0 then @@ -460,26 +504,28 @@ local function diagnostic_move_pos(opts, pos) return end - -- Save position in the window's jumplist - vim.api.nvim_win_call(win_id, function() vim.cmd("normal! m'") end) - - vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) + vim.api.nvim_win_call(win_id, function() + -- Save position in the window's jumplist + vim.cmd("normal! m'") + vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) + -- Open folds under the cursor + vim.cmd("normal! zv") + end) if float then local float_opts = type(float) == "table" and float or {} vim.schedule(function() M.open_float( - vim.api.nvim_win_get_buf(win_id), - vim.tbl_extend("keep", float_opts, {scope="cursor"}) + vim.tbl_extend("keep", float_opts, { + bufnr = vim.api.nvim_win_get_buf(win_id), + scope = "cursor", + focus = false, + }) ) end) end end --- }}} - --- Public API {{{ - --- Configure diagnostic options globally or for a specific diagnostic --- namespace. --- @@ -513,8 +559,10 @@ end --- - virtual_text: (default true) Use virtual text for diagnostics. Options: --- * severity: Only show virtual text for diagnostics matching the given --- severity |diagnostic-severity| ---- * source: (string) Include the diagnostic source in virtual ---- text. One of "always" or "if_many". +--- * source: (boolean or string) Include the diagnostic source in virtual +--- text. Use "if_many" to only show sources if there is more than +--- one diagnostic source in the buffer. Otherwise, any truthy value +--- means to always show the diagnostic source. --- * format: (function) A function that takes a diagnostic as input and --- returns a string. The return value is the text used to display --- the diagnostic. Example: @@ -532,19 +580,7 @@ end --- * priority: (number, default 10) Base priority to use for signs. When --- {severity_sort} is used, the priority of a sign is adjusted based on --- its severity. Otherwise, all signs use the same priority. ---- - float: Options for floating windows: ---- * severity: See |diagnostic-severity|. ---- * show_header: (boolean, default true) Show "Diagnostics:" header ---- * source: (string) Include the diagnostic source in ---- the message. One of "always" or "if_many". ---- * format: (function) A function that takes a diagnostic as input and returns a ---- string. The return value is the text used to display the diagnostic. ---- * prefix: (function or string) Prefix each diagnostic in the floating window. If ---- a function, it must have the signature (diagnostic, i, total) -> string, ---- where {i} is the index of the diagnostic being evaluated and {total} is ---- the total number of diagnostics displayed in the window. The returned ---- string is prepended to each diagnostic in the window. Otherwise, ---- if {prefix} is a string, it is prepended to each diagnostic. +--- - float: Options for floating windows. See |vim.diagnostic.open_float()|. --- - update_in_insert: (default false) Update diagnostics in Insert mode (if false, --- diagnostics are updated on InsertLeave) --- - severity_sort: (default false) Sort diagnostics by severity. This affects the order in @@ -552,6 +588,7 @@ end --- are displayed before lower severities (e.g. ERROR is displayed before WARN). --- Options: --- * reverse: (boolean) Reverse sort order +--- ---@param namespace number|nil Update the options for the given namespace. When omitted, update the --- global diagnostic options. function M.config(opts, namespace) @@ -605,33 +642,31 @@ function M.set(namespace, bufnr, diagnostics, opts) opts = {opts, 't', true}, } + bufnr = get_bufnr(bufnr) + if vim.tbl_isempty(diagnostics) then - clear_diagnostic_cache(namespace, bufnr) + diagnostic_cache[bufnr][namespace] = nil else - if not diagnostic_cleanup[bufnr][namespace] then - diagnostic_cleanup[bufnr][namespace] = true - - -- Clean up our data when the buffer unloads. - vim.api.nvim_buf_attach(bufnr, false, { - on_detach = function(_, b) - clear_diagnostic_cache(namespace, b) - diagnostic_cleanup[b][namespace] = nil - end - }) - end set_diagnostic_cache(namespace, bufnr, diagnostics) end if vim.api.nvim_buf_is_loaded(bufnr) then - M.show(namespace, bufnr, diagnostics, opts) + M.show(namespace, bufnr, nil, opts) end - vim.api.nvim_command("doautocmd <nomodeline> User DiagnosticsChanged") + vim.api.nvim_buf_call(bufnr, function() + vim.api.nvim_command( + string.format( + "doautocmd <nomodeline> DiagnosticChanged %s", + vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)) + ) + ) + end) end --- Get namespace metadata. --- ----@param ns number Diagnostic namespace +---@param namespace number Diagnostic namespace ---@return table Namespace metadata function M.get_namespace(namespace) vim.validate { namespace = { namespace, 'n' } } @@ -677,49 +712,7 @@ function M.get(bufnr, opts) opts = { opts, 't', true }, } - opts = opts or {} - - local namespace = opts.namespace - local diagnostics = {} - - ---@private - local function add(d) - if not opts.lnum or d.lnum == opts.lnum then - table.insert(diagnostics, d) - end - end - - if namespace == nil and bufnr == nil then - for _, t in pairs(diagnostic_cache) do - for _, v in pairs(t) do - for _, diagnostic in pairs(v) do - add(diagnostic) - end - end - end - elseif namespace == nil then - for iter_namespace in pairs(diagnostic_cache[bufnr]) do - for _, diagnostic in pairs(diagnostic_cache[bufnr][iter_namespace]) do - add(diagnostic) - end - end - elseif bufnr == nil then - for _, t in pairs(diagnostic_cache) do - for _, diagnostic in pairs(t[namespace] or {}) do - add(diagnostic) - end - end - else - for _, diagnostic in pairs(diagnostic_cache[bufnr][namespace] or {}) do - add(diagnostic) - end - end - - if opts.severity then - diagnostics = filter_by_severity(opts.severity, diagnostics) - end - - return diagnostics + return get_diagnostics(bufnr, opts, false) end --- Get the previous diagnostic closest to the cursor position. @@ -795,7 +788,9 @@ end --- - severity: See |diagnostic-severity|. --- - float: (boolean or table, default true) If "true", call |vim.diagnostic.open_float()| --- after moving. If a table, pass the table as the {opts} parameter to ---- |vim.diagnostic.open_float()|. +--- |vim.diagnostic.open_float()|. Unless overridden, the float will show +--- diagnostics at the new cursor position (as if "cursor" were passed to +--- the "scope" option). --- - win_id: (number, default 0) Window ID function M.goto_next(opts) return diagnostic_move_pos( @@ -931,8 +926,11 @@ M.handlers.virtual_text = { if opts.virtual_text.format then diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics) end - if opts.virtual_text.source then - diagnostics = prefix_source(opts.virtual_text.source, diagnostics) + if + opts.virtual_text.source + and (opts.virtual_text.source ~= "if_many" or count_sources(bufnr) > 1) + then + diagnostics = prefix_source(diagnostics) end if opts.virtual_text.severity then severity = opts.virtual_text.severity @@ -1039,19 +1037,22 @@ end --- ---@param namespace number|nil Diagnostic namespace. When omitted, hide --- diagnostics from all namespaces. ----@param bufnr number|nil Buffer number. Defaults to the current buffer. +---@param bufnr number|nil Buffer number, or 0 for current buffer. When +--- omitted, hide diagnostics in all buffers. function M.hide(namespace, bufnr) vim.validate { namespace = { namespace, 'n', true }, bufnr = { bufnr, 'n', true }, } - bufnr = get_bufnr(bufnr) - local namespaces = namespace and {namespace} or vim.tbl_keys(diagnostic_cache[bufnr]) - for _, iter_namespace in ipairs(namespaces) do - for _, handler in pairs(M.handlers) do - if handler.hide then - handler.hide(iter_namespace, bufnr) + local buffers = bufnr and {get_bufnr(bufnr)} or vim.tbl_keys(diagnostic_cache) + for _, iter_bufnr in ipairs(buffers) do + local namespaces = namespace and {namespace} or vim.tbl_keys(diagnostic_cache[iter_bufnr]) + for _, iter_namespace in ipairs(namespaces) do + for _, handler in pairs(M.handlers) do + if handler.hide then + handler.hide(iter_namespace, iter_bufnr) + end end end end @@ -1061,12 +1062,14 @@ end --- ---@param namespace number|nil Diagnostic namespace. When omitted, show --- diagnostics from all namespaces. ----@param bufnr number|nil Buffer number. Defaults to the current buffer. +---@param bufnr number|nil Buffer number, or 0 for current buffer. When omitted, show +--- diagnostics in all buffers. ---@param diagnostics table|nil The diagnostics to display. When omitted, use the --- saved diagnostics for the given namespace and --- buffer. This can be used to display a list of diagnostics --- without saving them or to display only a subset of ---- diagnostics. May not be used when {namespace} is nil. +--- diagnostics. May not be used when {namespace} +--- or {bufnr} is nil. ---@param opts table|nil Display options. See |vim.diagnostic.config()|. function M.show(namespace, bufnr, diagnostics, opts) vim.validate { @@ -1076,11 +1079,18 @@ function M.show(namespace, bufnr, diagnostics, opts) opts = { opts, 't', true }, } - bufnr = get_bufnr(bufnr) - if not namespace then - assert(not diagnostics, "Cannot show diagnostics without a namespace") - for iter_namespace in pairs(diagnostic_cache[bufnr]) do - M.show(iter_namespace, bufnr, nil, opts) + if not bufnr or not namespace then + assert(not diagnostics, "Cannot show diagnostics without a buffer and namespace") + if not bufnr then + for iter_bufnr in pairs(diagnostic_cache) do + M.show(namespace, iter_bufnr, nil, opts) + end + else + -- namespace is nil + bufnr = get_bufnr(bufnr) + for iter_namespace in pairs(diagnostic_cache[bufnr]) do + M.show(iter_namespace, bufnr, nil, opts) + end end return end @@ -1091,7 +1101,7 @@ function M.show(namespace, bufnr, diagnostics, opts) M.hide(namespace, bufnr) - diagnostics = diagnostics or M.get(bufnr, {namespace=namespace}) + diagnostics = diagnostics or get_diagnostics(bufnr, {namespace=namespace}, true) if not diagnostics or vim.tbl_isempty(diagnostics) then return @@ -1117,8 +1127,6 @@ function M.show(namespace, bufnr, diagnostics, opts) end end - clamp_line_numbers(bufnr, diagnostics) - for handler_name, handler in pairs(M.handlers) do if handler.show and opts[handler_name] then handler.show(namespace, bufnr, diagnostics, opts) @@ -1128,12 +1136,15 @@ end --- Show diagnostics in a floating window. --- ----@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param opts table|nil Configuration table with the same keys as --- |vim.lsp.util.open_floating_preview()| in addition to the following: +--- - bufnr: (number) Buffer number to show diagnostics from. +--- Defaults to the current buffer. --- - namespace: (number) Limit diagnostics to the given namespace ---- - scope: (string, default "buffer") Show diagnostics from the whole buffer ("buffer"), +--- - scope: (string, default "line") Show diagnostics from the whole buffer ("buffer"), --- the current cursor line ("line"), or the current cursor position ("cursor"). +--- Shorthand versions are also accepted ("c" for "cursor", "l" for "line", "b" +--- for "buffer"). --- - pos: (number or table) If {scope} is "line" or "cursor", use this position rather --- than the cursor position. If a number, interpreted as a line number; --- otherwise, a (row, col) tuple. @@ -1141,25 +1152,45 @@ end --- from |vim.diagnostic.config()|. --- - severity: See |diagnostic-severity|. Overrides the setting from --- |vim.diagnostic.config()|. ---- - show_header: (boolean, default true) Show "Diagnostics:" header. Overrides the ---- setting from |vim.diagnostic.config()|. ---- - source: (string) Include the diagnostic source in the message. One of "always" or ---- "if_many". Overrides the setting from |vim.diagnostic.config()|. +--- - header: (string or table) String to use as the header for the floating window. If a +--- table, it is interpreted as a [text, hl_group] tuple. Overrides the setting +--- from |vim.diagnostic.config()|. +--- - source: (boolean or string) Include the diagnostic source in the message. +--- Use "if_many" to only show sources if there is more than one source of +--- diagnostics in the buffer. Otherwise, any truthy value means to always show +--- the diagnostic source. Overrides the setting from +--- |vim.diagnostic.config()|. --- - format: (function) A function that takes a diagnostic as input and returns a --- string. The return value is the text used to display the diagnostic. --- Overrides the setting from |vim.diagnostic.config()|. ---- - prefix: (function or string) Prefix each diagnostic in the floating window. +--- - prefix: (function, string, or table) Prefix each diagnostic in the floating +--- window. If a function, it must have the signature (diagnostic, i, +--- total) -> (string, string), where {i} is the index of the diagnostic +--- being evaluated and {total} is the total number of diagnostics +--- displayed in the window. The function should return a string which +--- is prepended to each diagnostic in the window as well as an +--- (optional) highlight group which will be used to highlight the +--- prefix. If {prefix} is a table, it is interpreted as a [text, +--- hl_group] tuple as in |nvim_echo()|; otherwise, if {prefix} is a +--- string, it is prepended to each diagnostic in the window with no +--- highlight. --- Overrides the setting from |vim.diagnostic.config()|. ---@return tuple ({float_bufnr}, {win_id}) -function M.open_float(bufnr, opts) - vim.validate { - bufnr = { bufnr, 'n', true }, - opts = { opts, 't', true }, - } +function M.open_float(opts, ...) + -- Support old (bufnr, opts) signature + local bufnr + if opts == nil or type(opts) == "number" then + bufnr = opts + opts = ... + else + vim.validate { + opts = { opts, 't', true }, + } + end opts = opts or {} - bufnr = get_bufnr(bufnr) - local scope = opts.scope or "buffer" + bufnr = get_bufnr(bufnr or opts.bufnr) + local scope = ({l = "line", c = "cursor", b = "buffer"})[opts.scope] or opts.scope or "line" local lnum, col if scope == "line" or scope == "cursor" then if not opts.pos then @@ -1188,8 +1219,7 @@ function M.open_float(bufnr, opts) opts = get_resolved_options({ float = float_opts }, nil, bufnr).float end - local diagnostics = M.get(bufnr, opts) - clamp_line_numbers(bufnr, diagnostics) + local diagnostics = get_diagnostics(bufnr, opts, true) if scope == "line" then diagnostics = vim.tbl_filter(function(d) @@ -1220,35 +1250,56 @@ function M.open_float(bufnr, opts) local lines = {} local highlights = {} - local show_header = vim.F.if_nil(opts.show_header, true) - if show_header then - table.insert(lines, "Diagnostics:") - table.insert(highlights, {0, "Bold"}) + local header = if_nil(opts.header, "Diagnostics:") + if header then + vim.validate { header = { header, function(v) + return type(v) == "string" or type(v) == "table" + end, "'string' or 'table'" } } + if type(header) == "table" then + -- Don't insert any lines for an empty string + if string.len(if_nil(header[1], "")) > 0 then + table.insert(lines, header[1]) + table.insert(highlights, {0, header[2] or "Bold"}) + end + elseif #header > 0 then + table.insert(lines, header) + table.insert(highlights, {0, "Bold"}) + end end if opts.format then diagnostics = reformat_diagnostics(opts.format, diagnostics) end - if opts.source then - diagnostics = prefix_source(opts.source, diagnostics) + if opts.source and (opts.source ~= "if_many" or count_sources(bufnr) > 1) then + diagnostics = prefix_source(diagnostics) end local prefix_opt = if_nil(opts.prefix, (scope == "cursor" and #diagnostics <= 1) and "" or function(_, i) return string.format("%d. ", i) end) + + local prefix, prefix_hl_group if prefix_opt then vim.validate { prefix = { prefix_opt, function(v) - return type(v) == "string" or type(v) == "function" - end, "'string' or 'function'" } } + return type(v) == "string" or type(v) == "table" or type(v) == "function" + end, "'string' or 'table' or 'function'" } } + if type(prefix_opt) == "string" then + prefix, prefix_hl_group = prefix_opt, "NormalFloat" + elseif type(prefix_opt) == "table" then + prefix, prefix_hl_group = prefix_opt[1] or "", prefix_opt[2] or "NormalFloat" + end end for i, diagnostic in ipairs(diagnostics) do - local prefix = type(prefix_opt) == "string" and prefix_opt or prefix_opt(diagnostic, i, #diagnostics) + if prefix_opt and type(prefix_opt) == "function" then + prefix, prefix_hl_group = prefix_opt(diagnostic, i, #diagnostics) + prefix, prefix_hl_group = prefix or "", prefix_hl_group or "NormalFloat" + end local hiname = floating_highlight_map[diagnostic.severity] local message_lines = vim.split(diagnostic.message, '\n') table.insert(lines, prefix..message_lines[1]) - table.insert(highlights, {#prefix, hiname}) + table.insert(highlights, {#prefix, hiname, prefix_hl_group}) for j = 2, #message_lines do table.insert(lines, string.rep(' ', #prefix) .. message_lines[j]) table.insert(highlights, {0, hiname}) @@ -1261,8 +1312,10 @@ function M.open_float(bufnr, opts) end local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts) for i, hi in ipairs(highlights) do - local prefixlen, hiname = unpack(hi) - -- Start highlight after the prefix + local prefixlen, hiname, prefix_hiname = unpack(hi) + if prefix_hiname then + vim.api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i-1, 0, prefixlen) + end vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i-1, prefixlen, -1) end @@ -1286,16 +1339,23 @@ function M.reset(namespace, bufnr) bufnr = {bufnr, 'n', true}, } - local buffers = bufnr and {bufnr} or vim.tbl_keys(diagnostic_cache) + local buffers = bufnr and {get_bufnr(bufnr)} or vim.tbl_keys(diagnostic_cache) for _, iter_bufnr in ipairs(buffers) do local namespaces = namespace and {namespace} or vim.tbl_keys(diagnostic_cache[iter_bufnr]) for _, iter_namespace in ipairs(namespaces) do - clear_diagnostic_cache(iter_namespace, iter_bufnr) + diagnostic_cache[iter_bufnr][iter_namespace] = nil M.hide(iter_namespace, iter_bufnr) end end - vim.api.nvim_command("doautocmd <nomodeline> User DiagnosticsChanged") + vim.api.nvim_buf_call(bufnr, function() + vim.api.nvim_command( + string.format( + "doautocmd <nomodeline> DiagnosticChanged %s", + vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)) + ) + ) + end) end --- Add all diagnostics to the quickfix list. @@ -1323,44 +1383,67 @@ end --- Disable diagnostics in the given buffer. --- ----@param bufnr number|nil Buffer number. Defaults to the current buffer. +---@param bufnr number|nil Buffer number, or 0 for current buffer. When +--- omitted, disable diagnostics in all buffers. ---@param namespace number|nil Only disable diagnostics for the given namespace. function M.disable(bufnr, namespace) vim.validate { bufnr = {bufnr, 'n', true}, namespace = {namespace, 'n', true} } - bufnr = get_bufnr(bufnr) - if namespace == nil then - diagnostic_disabled[bufnr] = true - for ns in pairs(diagnostic_cache[bufnr]) do - M.hide(ns, bufnr) + if bufnr == nil then + if namespace == nil then + -- Disable everything (including as yet non-existing buffers and + -- namespaces) by setting diagnostic_disabled to an empty table and set + -- its metatable to always return true. This metatable is removed + -- in enable() + diagnostic_disabled = setmetatable({}, { + __index = function() return true end, + }) + else + local ns = M.get_namespace(namespace) + ns.disabled = true end else - if type(diagnostic_disabled[bufnr]) ~= "table" then - diagnostic_disabled[bufnr] = {} + bufnr = get_bufnr(bufnr) + if namespace == nil then + diagnostic_disabled[bufnr] = true + else + if type(diagnostic_disabled[bufnr]) ~= "table" then + diagnostic_disabled[bufnr] = {} + end + diagnostic_disabled[bufnr][namespace] = true end - diagnostic_disabled[bufnr][namespace] = true - M.hide(namespace, bufnr) end + + M.hide(namespace, bufnr) end --- Enable diagnostics in the given buffer. --- ----@param bufnr number|nil Buffer number. Defaults to the current buffer. +---@param bufnr number|nil Buffer number, or 0 for current buffer. When +--- omitted, enable diagnostics in all buffers. ---@param namespace number|nil Only enable diagnostics for the given namespace. function M.enable(bufnr, namespace) vim.validate { bufnr = {bufnr, 'n', true}, namespace = {namespace, 'n', true} } - bufnr = get_bufnr(bufnr) - if namespace == nil then - diagnostic_disabled[bufnr] = nil - for ns in pairs(diagnostic_cache[bufnr]) do - M.show(ns, bufnr) + if bufnr == nil then + if namespace == nil then + -- Enable everything by setting diagnostic_disabled to an empty table + diagnostic_disabled = {} + else + local ns = M.get_namespace(namespace) + ns.disabled = false end else - if type(diagnostic_disabled[bufnr]) ~= "table" then - return + bufnr = get_bufnr(bufnr) + if namespace == nil then + diagnostic_disabled[bufnr] = nil + else + if type(diagnostic_disabled[bufnr]) ~= "table" then + return + end + diagnostic_disabled[bufnr][namespace] = nil end - diagnostic_disabled[bufnr][namespace] = nil - M.show(namespace, bufnr) end + + M.show(namespace, bufnr) end --- Parse a diagnostic from a string. @@ -1474,7 +1557,7 @@ function M.fromqflist(list) for _, item in ipairs(list) do if item.valid == 1 then local lnum = math.max(0, item.lnum - 1) - local col = item.col > 0 and (item.col - 1) or nil + local col = math.max(0, item.col - 1) local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum local end_col = item.end_col > 0 and (item.end_col - 1) or col local severity = item.type ~= "" and M.severity[item.type] or M.severity.ERROR @@ -1492,6 +1575,4 @@ function M.fromqflist(list) return diagnostics end --- }}} - return M diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 0fc0a7a7aa..2e530ec17a 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -115,6 +115,13 @@ local format_line_ending = { ["mac"] = '\r', } +---@private +---@param bufnr (number) +---@returns (string) +local function buf_get_line_ending(bufnr) + return format_line_ending[nvim_buf_get_option(bufnr, 'fileformat')] or '\n' +end + local client_index = 0 ---@private --- Returns a new, unused client id. @@ -130,12 +137,6 @@ local all_buffer_active_clients = {} local uninitialized_clients = {} ---@private ---- Invokes a function for each LSP client attached to the buffer {bufnr}. ---- ----@param bufnr (Number) of buffer ----@param fn (function({client}, {client_id}, {bufnr}) Function to run on ----each client attached to that buffer. ----@param restrict_client_ids table list of client ids on which to restrict function application. local function for_each_buffer_client(bufnr, fn, restrict_client_ids) validate { fn = { fn, 'f' }; @@ -236,7 +237,6 @@ local function validate_client_config(config) config = { config, 't' }; } validate { - root_dir = { config.root_dir, optional_validator(is_dir), "directory" }; handlers = { config.handlers, "t", true }; capabilities = { config.capabilities, "t", true }; cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; @@ -278,9 +278,10 @@ end ---@param bufnr (number) Buffer handle, or 0 for current. ---@returns Buffer text as string. local function buf_get_full_text(bufnr) - local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') + local line_ending = buf_get_line_ending(bufnr) + local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), line_ending) if nvim_buf_get_option(bufnr, 'eol') then - text = text .. '\n' + text = text .. line_ending end return text end @@ -313,12 +314,14 @@ do --- --- state --- pending_change?: function that the timer starts to trigger didChange - --- pending_changes: list of tables with the pending changesets; for incremental_sync only + --- pending_changes: table (uri -> list of pending changeset tables)); + -- Only set if incremental_sync is used --- use_incremental_sync: bool --- buffers?: table (bufnr → lines); for incremental sync only --- timer?: uv_timer local state_by_client = {} + ---@private function changetracking.init(client, bufnr) local state = state_by_client[client.id] if not state then @@ -340,16 +343,16 @@ do state.buffers[bufnr] = nvim_buf_get_lines(bufnr, 0, -1, true) end + ---@private function changetracking.reset_buf(client, bufnr) + changetracking.flush(client) local state = state_by_client[client.id] - if state then - changetracking._reset_timer(state) - if state.buffers then - state.buffers[bufnr] = nil - end + if state and state.buffers then + state.buffers[bufnr] = nil end end + ---@private function changetracking.reset(client_id) local state = state_by_client[client_id] if state then @@ -358,13 +361,14 @@ do end end - function changetracking.prepare(bufnr, firstline, lastline, new_lastline, changedtick) + ---@private + function changetracking.prepare(bufnr, firstline, lastline, new_lastline) local incremental_changes = function(client) local cached_buffers = state_by_client[client.id].buffers local curr_lines = nvim_buf_get_lines(bufnr, 0, -1, true) - local line_ending = format_line_ending[vim.api.nvim_buf_get_option(0, 'fileformat')] + local line_ending = buf_get_line_ending(bufnr) local incremental_change = sync.compute_diff( - cached_buffers[bufnr], curr_lines, firstline, lastline, new_lastline, client.offset_encoding or 'utf-16', line_ending or '\n') + cached_buffers[bufnr], curr_lines, firstline, lastline, new_lastline, client.offset_encoding or 'utf-16', line_ending) cached_buffers[bufnr] = curr_lines return incremental_change end @@ -385,7 +389,7 @@ do client.notify("textDocument/didChange", { textDocument = { uri = uri; - version = changedtick; + version = util.buf_versions[bufnr]; }; contentChanges = { changes, } }) @@ -395,27 +399,36 @@ do if state.use_incremental_sync then -- This must be done immediately and cannot be delayed -- The contents would further change and startline/endline may no longer fit - table.insert(state.pending_changes, incremental_changes(client)) + if not state.pending_changes[uri] then + state.pending_changes[uri] = {} + end + table.insert(state.pending_changes[uri], incremental_changes(client)) end state.pending_change = function() state.pending_change = nil if client.is_stopped() or not vim.api.nvim_buf_is_valid(bufnr) then return end - local contentChanges if state.use_incremental_sync then - contentChanges = state.pending_changes + for change_uri, content_changes in pairs(state.pending_changes) do + client.notify("textDocument/didChange", { + textDocument = { + uri = change_uri; + version = util.buf_versions[vim.uri_to_bufnr(change_uri)]; + }; + contentChanges = content_changes, + }) + end state.pending_changes = {} else - contentChanges = { full_changes(), } + client.notify("textDocument/didChange", { + textDocument = { + uri = uri; + version = util.buf_versions[bufnr]; + }; + contentChanges = { full_changes() }, + }) end - client.notify("textDocument/didChange", { - textDocument = { - uri = uri; - version = changedtick; - }; - contentChanges = contentChanges - }) end state.timer = vim.loop.new_timer() -- Must use schedule_wrap because `full_changes()` calls nvim_buf_get_lines @@ -432,6 +445,7 @@ do end --- Flushes any outstanding change notification. + ---@private function changetracking.flush(client) local state = state_by_client[client.id] if state then @@ -475,7 +489,8 @@ local function text_document_did_open_handler(bufnr, client) -- Protect against a race where the buffer disappears -- between `did_open_handler` and the scheduled function firing. if vim.api.nvim_buf_is_valid(bufnr) then - vim.lsp.diagnostic.redraw(bufnr, client.id) + local namespace = vim.lsp.diagnostic.get_namespace(client.id) + vim.diagnostic.show(namespace, bufnr) end end) end @@ -545,6 +560,12 @@ end --- --- - {handlers} (table): The handlers used by the client as described in |lsp-handler|. --- +--- - {requests} (table): The current pending requests in flight +--- to the server. Entries are key-value pairs with the key +--- being the request ID while the value is a table with `type`, +--- `bufnr`, and `method` key-value pairs. `type` is either "pending" +--- for an active request, or "cancel" for a cancel request. +--- --- - {config} (table): copy of the table that was passed by the user --- to |vim.lsp.start_client()|. --- @@ -566,12 +587,10 @@ end -- --- Starts and initializes a client with the given configuration. --- ---- Parameters `cmd` and `root_dir` are required. +--- Parameter `cmd` is required. --- --- The following parameters describe fields in the {config} table. --- ----@param root_dir: (string) Directory where the LSP server will base ---- its rootUri on initialization. --- ---@param cmd: (required, string or list treated like |jobstart()|) Base command --- that initiates the LSP client. @@ -587,6 +606,11 @@ end --- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; } --- </pre> --- +---@param workspace_folders (table) List of workspace folders passed to the +--- language server. For backwards compatibility rootUri and rootPath will be +--- derived from the first workspace folder in this list. See `workspaceFolders` in +--- the LSP spec. +--- ---@param capabilities Map overriding the default capabilities defined by --- |vim.lsp.protocol.make_client_capabilities()|, passed to the language --- server on initialization. Hint: use make_client_capabilities() and modify @@ -603,17 +627,13 @@ end --- ---@param commands table Table that maps string of clientside commands to user-defined functions. --- Commands passed to start_client take precedence over the global command registry. Each key ---- must be a unique comand name, and the value is a function which is called if any LSP action +--- must be a unique command name, and the value is a function which is called if any LSP action --- (code action, code lenses, ...) triggers the command. --- ---@param init_options Values to pass in the initialization request --- as `initializationOptions`. See `initialize` in the LSP spec. --- ---@param name (string, default=client-id) Name in log messages. --- ----@param workspace_folders (table) List of workspace folders passed to the ---- language server. Defaults to root_dir if not set. See `workspaceFolders` in ---- the LSP spec --- ---@param get_language_id function(bufnr, filetype) -> language ID as string. --- Defaults to the filetype. @@ -661,8 +681,13 @@ end --- notifications to the server by the given number in milliseconds. No debounce --- occurs if nil --- - exit_timeout (number, default 500): Milliseconds to wait for server to --- exit cleanly after sending the 'shutdown' request before sending kill -15. --- If set to false, nvim exits immediately after sending the 'shutdown' request to the server. +--- exit cleanly after sending the 'shutdown' request before sending kill -15. +--- If set to false, nvim exits immediately after sending the 'shutdown' request to the server. +--- +---@param root_dir string Directory where the LSP +--- server will base its workspaceFolders, rootUri, and rootPath +--- on initialization. +--- ---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once --- the client has been initialized. @@ -752,6 +777,10 @@ function lsp.start_client(config) ---@param code (number) exit code of the process ---@param signal (number) the signal used to terminate (if any) function dispatch.on_exit(code, signal) + if config.on_exit then + pcall(config.on_exit, code, signal, client_id) + end + active_clients[client_id] = nil uninitialized_clients[client_id] = nil @@ -767,10 +796,6 @@ function lsp.start_client(config) vim.notify(msg, vim.log.levels.WARN) end) end - - if config.on_exit then - pcall(config.on_exit, code, signal, client_id) - end end -- Start the RPC client. @@ -779,6 +804,9 @@ function lsp.start_client(config) env = config.cmd_env; }) + -- Return nil if client fails to start + if not rpc then return end + local client = { id = client_id; name = name; @@ -805,11 +833,24 @@ function lsp.start_client(config) } local version = vim.version() - if config.root_dir and not config.workspace_folders then - config.workspace_folders = {{ - uri = vim.uri_from_fname(config.root_dir); - name = string.format("%s", config.root_dir); - }}; + local workspace_folders + local root_uri + local root_path + if config.workspace_folders or config.root_dir then + if config.root_dir and not config.workspace_folders then + workspace_folders = {{ + uri = vim.uri_from_fname(config.root_dir); + name = string.format("%s", config.root_dir); + }}; + else + workspace_folders = config.workspace_folders + end + root_uri = workspace_folders[1].uri + root_path = vim.uri_to_fname(root_uri) + else + workspace_folders = nil + root_uri = nil + root_path = nil end local initialize_params = { @@ -827,10 +868,15 @@ function lsp.start_client(config) -- The rootPath of the workspace. Is null if no folder is open. -- -- @deprecated in favour of rootUri. - rootPath = config.root_dir; + rootPath = root_path or vim.NIL; -- The rootUri of the workspace. Is null if no folder is open. If both -- `rootPath` and `rootUri` are set `rootUri` wins. - rootUri = config.root_dir and vim.uri_from_fname(config.root_dir); + rootUri = root_uri or vim.NIL; + -- The workspace folders configured in the client when the server starts. + -- This property is only available if the client supports workspace folders. + -- It can be `null` if the client supports workspace folders but none are + -- configured. + workspaceFolders = workspace_folders or vim.NIL; -- User provided initialization options. initializationOptions = config.init_options; -- The capabilities provided by the client (editor or tool) @@ -838,21 +884,6 @@ function lsp.start_client(config) -- The initial trace setting. If omitted trace is disabled ("off"). -- trace = "off" | "messages" | "verbose"; trace = valid_traces[config.trace] or 'off'; - -- The workspace folders configured in the client when the server starts. - -- This property is only available if the client supports workspace folders. - -- It can be `null` if the client supports workspace folders but none are - -- configured. - -- - -- Since 3.6.0 - -- workspaceFolders?: WorkspaceFolder[] | null; - -- export interface WorkspaceFolder { - -- -- The associated URI for this workspace folder. - -- uri - -- -- The name of the workspace folder. Used to refer to this - -- -- workspace folder in the user interface. - -- name - -- } - workspaceFolders = config.workspace_folders, } if config.before_init then -- TODO(ashkan) handle errors here. @@ -865,7 +896,9 @@ function lsp.start_client(config) rpc.notify('initialized', vim.empty_dict()) client.initialized = true uninitialized_clients[client_id] = nil - client.workspaceFolders = initialize_params.workspaceFolders + client.workspace_folders = workspace_folders + -- TODO(mjlbach): Backwards compatbility, to be removed in 0.7 + client.workspaceFolders = client.workspace_folders client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities") -- These are the cleaned up capabilities we use for dynamically deciding -- when to send certain events to clients. @@ -1007,7 +1040,7 @@ function lsp.start_client(config) return rpc.notify("$/cancelRequest", { id = id }) end - -- Track this so that we can escalate automatically if we've alredy tried a + -- Track this so that we can escalate automatically if we've already tried a -- graceful shutdown local graceful_shutdown_failed = false ---@private @@ -1084,7 +1117,7 @@ do return end util.buf_versions[bufnr] = changedtick - local compute_change_and_notify = changetracking.prepare(bufnr, firstline, lastline, new_lastline, changedtick) + local compute_change_and_notify = changetracking.prepare(bufnr, firstline, lastline, new_lastline) for_each_buffer_client(bufnr, compute_change_and_notify) end end @@ -1143,6 +1176,7 @@ function lsp.buf_attach_client(bufnr, client_id) on_reload = function() local params = { textDocument = { uri = uri; } } for_each_buffer_client(bufnr, function(client, _) + changetracking.reset_buf(client, bufnr) if client.resolved_capabilities.text_document_open_close then client.notify('textDocument/didClose', params) end @@ -1152,10 +1186,10 @@ function lsp.buf_attach_client(bufnr, client_id) on_detach = function() local params = { textDocument = { uri = uri; } } for_each_buffer_client(bufnr, function(client, _) + changetracking.reset_buf(client, bufnr) if client.resolved_capabilities.text_document_open_close then client.notify('textDocument/didClose', params) end - changetracking.reset_buf(client, bufnr) end) util.buf_versions[bufnr] = nil all_buffer_active_clients[bufnr] = nil @@ -1190,7 +1224,7 @@ end --- Gets a client by id, or nil if the id is invalid. --- The returned client may not yet be fully initialized. --- +--- ---@param client_id number client id --- ---@returns |vim.lsp.client| object, or nil @@ -1199,7 +1233,7 @@ function lsp.get_client_by_id(client_id) end --- Returns list of buffers attached to client_id. --- +--- ---@param client_id number client id ---@returns list of buffer ids function lsp.get_buffers_by_client_id(client_id) @@ -1269,6 +1303,7 @@ function lsp._vim_exit_handler() local poll_time = 50 + ---@private local function check_clients_closed() for client_id, timeout in pairs(timeouts) do timeouts[client_id] = timeout - poll_time @@ -1303,8 +1338,8 @@ nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()") ---@param method (string) LSP method name ---@param params (optional, table) Parameters to send to the server ---@param handler (optional, function) See |lsp-handler| --- If nil, follows resolution strategy defined in |lsp-handler-configuration| --- +--- If nil, follows resolution strategy defined in |lsp-handler-configuration| +--- ---@returns 2-tuple: --- - Map of client-id:request-id pairs for all successful requests. --- - Function which can be used to cancel all the requests. You could instead @@ -1458,11 +1493,7 @@ local function adjust_start_col(lnum, line, items, encoding) end end if min_start_char then - if encoding == 'utf-8' then - return min_start_char - else - return vim.str_byteindex(line, min_start_char, encoding == 'utf-16') - end + return util._str_byteindex_enc(line, min_start_char, encoding) else return nil end @@ -1587,6 +1618,21 @@ function lsp.formatexpr(opts) return 0 end +--- Provides an interface between the built-in client and 'tagfunc'. +--- +--- When used with normal mode commands (e.g. |CTRL-]|) this will invoke +--- the "textDocument/definition" LSP method to find the tag under the cursor. +--- Otherwise, uses "workspace/symbol". If no results are returned from +--- any LSP servers, falls back to using built-in tags. +--- +---@param pattern Pattern used to find a workspace symbol +---@param flags See |tag-function| +--- +---@returns A list of matching tags +function lsp.tagfunc(...) + return require('vim.lsp.tagfunc')(...) +end + ---Checks whether a client is stopped. --- ---@param client_id (Number) @@ -1640,7 +1686,17 @@ function lsp.get_log_path() return log.get_filename() end ---- Call {fn} for every client attached to {bufnr} +--- Invokes a function for each LSP client attached to a buffer. +--- +---@param bufnr number Buffer number +---@param fn function Function to run on each client attached to buffer +--- {bufnr}. The function takes the client, client ID, and +--- buffer number as arguments. Example: +--- <pre> +--- vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr) +--- print(vim.inspect(client)) +--- end) +--- </pre> function lsp.for_each_buffer_client(bufnr, fn) return for_each_buffer_client(bufnr, fn) end @@ -1699,11 +1755,11 @@ end --- using `workspace/executeCommand`. --- --- The first argument to the function will be the `Command`: --- Command --- title: String --- command: String --- arguments?: any[] --- +--- Command +--- title: String +--- command: String +--- arguments?: any[] +--- --- The second argument is the `ctx` of |lsp-handler| lsp.commands = setmetatable({}, { __newindex = function(tbl, key, value) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 747d761730..8e3ed9b002 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -116,31 +116,30 @@ end --- asks the user to select one. -- ---@returns The client that the user selected or nil -local function select_client(method) - local clients = vim.tbl_values(vim.lsp.buf_get_clients()); - clients = vim.tbl_filter(function (client) +local function select_client(method, on_choice) + validate { + on_choice = { on_choice, 'function', false }, + } + local clients = vim.tbl_values(vim.lsp.buf_get_clients()) + clients = vim.tbl_filter(function(client) return client.supports_method(method) end, clients) -- better UX when choices are always in the same order (between restarts) - table.sort(clients, function (a, b) return a.name < b.name end) + table.sort(clients, function(a, b) + return a.name < b.name + end) if #clients > 1 then - local choices = {} - for k,v in pairs(clients) do - table.insert(choices, string.format("%d %s", k, v.name)) - end - local user_choice = vim.fn.confirm( - "Select a language server:", - table.concat(choices, "\n"), - 0, - "Question" - ) - if user_choice == 0 then return nil end - return clients[user_choice] + vim.ui.select(clients, { + prompt = 'Select a language server:', + format_item = function(client) + return client.name + end, + }, on_choice) elseif #clients < 1 then - return nil + on_choice(nil) else - return clients[1] + on_choice(clients[1]) end end @@ -152,11 +151,15 @@ end -- ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting function M.formatting(options) - local client = select_client("textDocument/formatting") - if client == nil then return end - local params = util.make_formatting_params(options) - return client.request("textDocument/formatting", params, nil, vim.api.nvim_get_current_buf()) + local bufnr = vim.api.nvim_get_current_buf() + select_client('textDocument/formatting', function(client) + if client == nil then + return + end + + return client.request('textDocument/formatting', params, nil, bufnr) + end) end --- Performs |vim.lsp.buf.formatting()| synchronously. @@ -165,23 +168,27 @@ end --- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. Example: --- --- <pre> ---- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()]] +--- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync() --- </pre> --- ---@param options Table with valid `FormattingOptions` entries ---@param timeout_ms (number) Request timeout ---@see |vim.lsp.buf.formatting_seq_sync| function M.formatting_sync(options, timeout_ms) - local client = select_client("textDocument/formatting") - if client == nil then return end - local params = util.make_formatting_params(options) - local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf()) - if result and result.result then - util.apply_text_edits(result.result) - elseif err then - vim.notify("vim.lsp.buf.formatting_sync: " .. err, vim.log.levels.WARN) - end + local bufnr = vim.api.nvim_get_current_buf() + select_client('textDocument/formatting', function(client) + if client == nil then + return + end + + local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr) + if result and result.result then + util.apply_text_edits(result.result, bufnr) + elseif err then + vim.notify('vim.lsp.buf.formatting_sync: ' .. err, vim.log.levels.WARN) + end + end) end --- Formats the current buffer by sequentially requesting formatting from attached clients. @@ -202,6 +209,7 @@ end ---the remaining clients in the order as they occur in the `order` list. function M.formatting_seq_sync(options, timeout_ms, order) local clients = vim.tbl_values(vim.lsp.buf_get_clients()); + local bufnr = vim.api.nvim_get_current_buf() -- sort the clients according to `order` for _, client_name in pairs(order or {}) do @@ -220,7 +228,7 @@ function M.formatting_seq_sync(options, timeout_ms, order) local params = util.make_formatting_params(options) local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf()) if result and result.result then - util.apply_text_edits(result.result) + util.apply_text_edits(result.result, bufnr) elseif err then vim.notify(string.format("vim.lsp.buf.formatting_seq_sync: (%s) %s", client.name, err), vim.log.levels.WARN) end @@ -236,12 +244,15 @@ end ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_formatting(options, start_pos, end_pos) - local client = select_client("textDocument/rangeFormatting") - if client == nil then return end - local params = util.make_given_range_params(start_pos, end_pos) params.options = util.make_formatting_params(options).options - return client.request("textDocument/rangeFormatting", params) + select_client('textDocument/rangeFormatting', function(client) + if client == nil then + return + end + + return client.request('textDocument/rangeFormatting', params) + end) end --- Renames all references to the symbol under the cursor. @@ -261,6 +272,7 @@ function M.rename(new_name) request('textDocument/rename', params) end + ---@private local function prepare_rename(err, result) if err == nil and result == nil then vim.notify('nothing to rename', vim.log.levels.INFO) @@ -369,7 +381,7 @@ end function M.list_workspace_folders() local workspace_folders = {} for _, client in pairs(vim.lsp.buf_get_clients()) do - for _, folder in pairs(client.workspaceFolders or {}) do + for _, folder in pairs(client.workspace_folders or {}) do table.insert(workspace_folders, folder.name) end end @@ -389,7 +401,7 @@ function M.add_workspace_folder(workspace_folder) local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}}) for _, client in pairs(vim.lsp.buf_get_clients()) do local found = false - for _, folder in pairs(client.workspaceFolders or {}) do + for _, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then found = true print(workspace_folder, "is already part of this workspace") @@ -398,10 +410,10 @@ function M.add_workspace_folder(workspace_folder) end if not found then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) - if not client.workspaceFolders then - client.workspaceFolders = {} + if not client.workspace_folders then + client.workspace_folders = {} end - table.insert(client.workspaceFolders, params.event.added[1]) + table.insert(client.workspace_folders, params.event.added[1]) end end end @@ -415,10 +427,10 @@ function M.remove_workspace_folder(workspace_folder) if not (workspace_folder and #workspace_folder > 0) then return end local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}) for _, client in pairs(vim.lsp.buf_get_clients()) do - for idx, folder in pairs(client.workspaceFolders) do + for idx, folder in pairs(client.workspace_folders) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) - client.workspaceFolders[idx] = nil + client.workspace_folders[idx] = nil return end end @@ -444,9 +456,9 @@ end --- by events such as `CursorHold`, eg: --- --- <pre> ---- vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]] ---- vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]] ---- vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]] +--- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight() +--- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight() +--- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references() --- </pre> --- --- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 1e6f83c1ba..8850d25233 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -153,19 +153,6 @@ function M.get_namespace(client_id) return _client_namespaces[client_id] end ---- Save diagnostics to the current buffer. ---- ---- Handles saving diagnostics from multiple clients in the same buffer. ----@param diagnostics Diagnostic[] ----@param bufnr number ----@param client_id number ----@private -function M.save(diagnostics, bufnr, client_id) - local namespace = M.get_namespace(client_id) - vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) -end --- }}} - --- |lsp-handler| for the method "textDocument/publishDiagnostics" --- --- See |vim.diagnostic.config()| for configuration options. Handler-specific @@ -220,12 +207,9 @@ function M.on_publish_diagnostics(_, result, ctx, config) end vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) - - -- Keep old autocmd for back compat. This should eventually be removed. - vim.api.nvim_command("doautocmd <nomodeline> User LspDiagnosticsChanged") end ---- Clear diagnotics and diagnostic cache. +--- Clear diagnostics and diagnostic cache. --- --- Diagnostic producers should prefer |vim.diagnostic.reset()|. However, --- this method signature is still used internally in some parts of the LSP @@ -248,6 +232,23 @@ end -- Deprecated Functions {{{ + +--- Save diagnostics to the current buffer. +--- +---@deprecated Prefer |vim.diagnostic.set()| +--- +--- Handles saving diagnostics from multiple clients in the same buffer. +---@param diagnostics Diagnostic[] +---@param bufnr number +---@param client_id number +---@private +function M.save(diagnostics, bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.save is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) + local namespace = M.get_namespace(client_id) + vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) +end +-- }}} + --- Get all diagnostics for clients --- ---@deprecated Prefer |vim.diagnostic.get()| @@ -256,6 +257,7 @@ end --- If nil, diagnostics of all clients are included. ---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[]) function M.get_all(client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_all is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local result = {} local namespace if client_id then @@ -277,6 +279,7 @@ end --- Else, return just the diagnostics associated with the client_id. ---@param predicate function|nil Optional function for filtering diagnostics function M.get(bufnr, client_id, predicate) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) predicate = predicate or function() return true end if client_id == nil then local all_diagnostics = {} @@ -338,6 +341,7 @@ end ---@param severity DiagnosticSeverity ---@param client_id number the client id function M.get_count(bufnr, severity, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_count is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) severity = severity_lsp_to_vim(severity) local opts = { severity = severity } if client_id ~= nil then @@ -354,6 +358,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic function M.get_prev(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -371,6 +376,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic position function M.get_prev_pos(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -387,6 +393,7 @@ end --- ---@param opts table See |vim.lsp.diagnostic.goto_next()| function M.goto_prev(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -404,6 +411,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic function M.get_next(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -421,6 +429,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic position function M.get_next_pos(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -434,25 +443,8 @@ end --- Move to the next diagnostic --- ---@deprecated Prefer |vim.diagnostic.goto_next()| ---- ----@param opts table|nil Configuration table. Keys: ---- - {client_id}: (number) ---- - If nil, will consider all clients attached to buffer. ---- - {cursor_position}: (Position, default current position) ---- - See |nvim_win_get_cursor()| ---- - {wrap}: (boolean, default true) ---- - Whether to loop around file or not. Similar to 'wrapscan' ---- - {severity}: (DiagnosticSeverity) ---- - Exclusive severity to consider. Overrides {severity_limit} ---- - {severity_limit}: (DiagnosticSeverity) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - {enable_popup}: (boolean, default true) ---- - Call |vim.lsp.diagnostic.show_line_diagnostics()| on jump ---- - {popup_opts}: (table) ---- - Table to pass as {opts} parameter to |vim.lsp.diagnostic.show_line_diagnostics()| ---- - {win_id}: (number, default 0) ---- - Window ID function M.goto_next(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -476,6 +468,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_signs(diagnostics, bufnr, client_id, _, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_signs is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -496,6 +489,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_underline(diagnostics, bufnr, client_id, _, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_underline is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -517,6 +511,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_virtual_text is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -535,6 +530,7 @@ end ---@return an array of [text, hl_group] arrays. This can be passed directly to --- the {virt_text} option of |nvim_buf_set_extmark()|. function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_virtual_text_chunks_for_line is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts) end @@ -552,6 +548,7 @@ end ---@param position table|nil The (0,0)-indexed position ---@return table {popup_bufnr, win_id} function M.show_position_diagnostics(opts, buf_nr, position) + vim.api.nvim_echo({{'vim.lsp.diagnostic.show_position_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} opts.scope = "cursor" opts.pos = position @@ -565,7 +562,7 @@ end --- Open a floating window with the diagnostics from {line_nr} --- ----@deprecated Prefer |vim.diagnostic.show_line_diagnostics()| +---@deprecated Prefer |vim.diagnostic.open_float()| --- ---@param opts table Configuration table --- - all opts for |vim.lsp.diagnostic.get_line_diagnostics()| and @@ -575,6 +572,7 @@ end ---@param client_id number|nil the client id ---@return table {popup_bufnr, win_id} function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.show_line_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} opts.scope = "line" opts.pos = line_nr @@ -586,7 +584,7 @@ end --- Redraw diagnostics for the given buffer and client --- ----@deprecated Prefer |vim.diagnostic.redraw()| +---@deprecated Prefer |vim.diagnostic.show()| --- --- This calls the "textDocument/publishDiagnostics" handler manually using --- the cached diagnostics already received from the server. This can be useful @@ -598,6 +596,7 @@ end --- client. The default is to redraw diagnostics for all attached --- clients. function M.redraw(bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.redraw is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) bufnr = get_bufnr(bufnr) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) @@ -625,6 +624,7 @@ end --- - {workspace}: (boolean, default true) --- - Set the list with workspace diagnostics function M.set_qflist(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -656,6 +656,7 @@ end --- - {workspace}: (boolean, default false) --- - Set the list with workspace diagnostics function M.set_loclist(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -683,6 +684,7 @@ end -- send diagnostic information and the client will still process it. The -- diagnostics are simply not displayed to the user. function M.disable(bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.disable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.disable(bufnr, client.id) @@ -703,6 +705,7 @@ end --- client. The default is to enable diagnostics for all attached --- clients. function M.enable(bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.enable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.enable(bufnr, client.id) @@ -717,5 +720,3 @@ end -- }}} return M - --- vim: fdm=marker diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a561630c2b..c974d1a6b4 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -28,7 +28,7 @@ local function progress_handler(_, result, ctx, _) local client_name = client and client.name or string.format("id=%d", client_id) if not client then err_message("LSP[", client_name, "] client has shut down after sending the message") - return + return vim.NIL end local val = result.value -- unspecified yet local token = result.token -- string or number @@ -70,6 +70,7 @@ M['window/workDoneProgress/create'] = function(_, result, ctx) local client_name = client and client.name or string.format("id=%d", client_id) if not client then err_message("LSP[", client_name, "] client has shut down after sending the message") + return vim.NIL end client.messages.progress[token] = {} return vim.NIL @@ -284,7 +285,7 @@ local function location_handler(_, result, ctx, _) util.jump_to_location(result[1]) if #result > 1 then - util.set_qflist(util.locations_to_items(result)) + vim.fn.setqflist({}, ' ', {title = 'LSP locations', items = util.locations_to_items(result)}) api.nvim_command("copen") end else @@ -350,7 +351,10 @@ M['textDocument/signatureHelp'] = M.signature_help --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight M['textDocument/documentHighlight'] = function(_, result, ctx, _) if not result then return end - util.buf_highlight_references(ctx.bufnr, result, ctx.client_id) + local client_id = ctx.client_id + local client = vim.lsp.get_client_by_id(client_id) + if not client then return end + util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding) end ---@private @@ -375,7 +379,7 @@ local make_call_hierarchy_handler = function(direction) }) end end - util.set_qflist(items) + vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items}) api.nvim_command("copen") end end diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 4597f1919a..dbc473b52c 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -101,6 +101,7 @@ function log.set_level(level) end --- Gets the current log level. +---@return string current log level function log.get_level() return current_log_level end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index b3aa8b934f..86c9e2fd58 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -776,149 +776,9 @@ function protocol.make_client_capabilities() } end ---[=[ -export interface DocumentFilter { - --A language id, like `typescript`. - language?: string; - --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. - scheme?: string; - --A glob pattern, like `*.{ts,js}`. - -- - --Glob patterns can have the following syntax: - --- `*` to match one or more characters in a path segment - --- `?` to match on one character in a path segment - --- `**` to match any number of path segments, including none - --- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) - --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) - pattern?: string; -} ---]=] - ---[[ ---Static registration options to be returned in the initialize request. -interface StaticRegistrationOptions { - --The id used to register the request. The id can be used to deregister - --the request again. See also Registration#id. - id?: string; -} - -export interface DocumentFilter { - --A language id, like `typescript`. - language?: string; - --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. - scheme?: string; - --A glob pattern, like `*.{ts,js}`. - -- - --Glob patterns can have the following syntax: - --- `*` to match one or more characters in a path segment - --- `?` to match on one character in a path segment - --- `**` to match any number of path segments, including none - --- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) - --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) - pattern?: string; -} -export type DocumentSelector = DocumentFilter[]; -export interface TextDocumentRegistrationOptions { - --A document selector to identify the scope of the registration. If set to null - --the document selector provided on the client side will be used. - documentSelector: DocumentSelector | null; -} - ---Code Action options. -export interface CodeActionOptions { - --CodeActionKinds that this server may return. - -- - --The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server - --may list out every specific kind they provide. - codeActionKinds?: CodeActionKind[]; -} - -interface ServerCapabilities { - --Defines how text documents are synced. Is either a detailed structure defining each notification or - --for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`. - textDocumentSync?: TextDocumentSyncOptions | number; - --The server provides hover support. - hoverProvider?: boolean; - --The server provides completion support. - completionProvider?: CompletionOptions; - --The server provides signature help support. - signatureHelpProvider?: SignatureHelpOptions; - --The server provides goto definition support. - definitionProvider?: boolean; - --The server provides Goto Type Definition support. - -- - --Since 3.6.0 - typeDefinitionProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides Goto Implementation support. - -- - --Since 3.6.0 - implementationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides find references support. - referencesProvider?: boolean; - --The server provides document highlight support. - documentHighlightProvider?: boolean; - --The server provides document symbol support. - documentSymbolProvider?: boolean; - --The server provides workspace symbol support. - workspaceSymbolProvider?: boolean; - --The server provides code actions. The `CodeActionOptions` return type is only - --valid if the client signals code action literal support via the property - --`textDocument.codeAction.codeActionLiteralSupport`. - codeActionProvider?: boolean | CodeActionOptions; - --The server provides code lens. - codeLensProvider?: CodeLensOptions; - --The server provides document formatting. - documentFormattingProvider?: boolean; - --The server provides document range formatting. - documentRangeFormattingProvider?: boolean; - --The server provides document formatting on typing. - documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions; - --The server provides rename support. RenameOptions may only be - --specified if the client states that it supports - --`prepareSupport` in its initial `initialize` request. - renameProvider?: boolean | RenameOptions; - --The server provides document link support. - documentLinkProvider?: DocumentLinkOptions; - --The server provides color provider support. - -- - --Since 3.6.0 - colorProvider?: boolean | ColorProviderOptions | (ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides folding provider support. - -- - --Since 3.10.0 - foldingRangeProvider?: boolean | FoldingRangeProviderOptions | (FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides go to declaration support. - -- - --Since 3.14.0 - declarationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides execute command support. - executeCommandProvider?: ExecuteCommandOptions; - --Workspace specific server capabilities - workspace?: { - --The server supports workspace folder. - -- - --Since 3.6.0 - workspaceFolders?: { - * The server has support for workspace folders - supported?: boolean; - * Whether the server wants to receive workspace folder - * change notifications. - * - * If a strings is provided the string is treated as a ID - * under which the notification is registered on the client - * side. The ID can be used to unregister for these events - * using the `client/unregisterCapability` request. - changeNotifications?: string | boolean; - } - } - --Experimental server capabilities. - experimental?: any; -} ---]] - --- Creates a normalized object describing LSP server capabilities. +---@param server_capabilities table Table of capabilities supported by the server +---@return table Normalized table of capabilities function protocol.resolve_capabilities(server_capabilities) local general_properties = {} local text_document_sync_properties diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index bce1e9f35d..1fb75ddeb7 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -230,7 +230,7 @@ function default_dispatchers.on_error(code, err) end --- Starts an LSP server process and create an LSP RPC client object to ---- interact with it. +--- interact with it. Communication with the server is currently limited to stdio. --- ---@param cmd (string) Command to start the LSP server. ---@param cmd_args (table) List of additional string arguments to pass to {cmd}. @@ -264,8 +264,6 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) if extra_spawn_params and extra_spawn_params.cwd then assert(is_dir(extra_spawn_params.cwd), "cwd must be a directory") - elseif not (vim.fn.executable(cmd) == 1) then - error(string.format("The given command %q is not executable.", cmd)) end if dispatchers then local user_dispatchers = dispatchers @@ -325,7 +323,14 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end handle, pid = uv.spawn(cmd, spawn_params, onexit) if handle == nil then - error(string.format("start `%s` failed: %s", cmd, pid)) + local msg = string.format("Spawning language server with cmd: `%s` failed", cmd) + if string.match(pid, "ENOENT") then + msg = msg .. ". The language server is either not installed, missing from PATH, or not executable." + else + msg = msg .. string.format(" with error message: %s", pid) + end + vim.notify(msg, vim.log.levels.WARN) + return end end diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 37247c61b9..d01f45ad8f 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -75,42 +75,46 @@ local function byte_to_utf(line, byte, offset_encoding) end ---@private +local function compute_line_length(line, offset_encoding) + local length + local _ + if offset_encoding == 'utf-16' then + _, length = str_utfindex(line) + elseif offset_encoding == 'utf-32' then + length, _ = str_utfindex(line) + else + length = #line + end + return length +end + +---@private -- Given a line, byte idx, alignment, and offset_encoding convert to the aligned -- utf-8 index and either the utf-16, or utf-32 index. ---@param line string the line to index into ---@param byte integer the byte idx ----@param align string when dealing with multibyte characters, --- to choose the start of the current character or the beginning of the next. --- Used for incremental sync for start/end range respectively ---@param offset_encoding string utf-8|utf-16|utf-32|nil (default: utf-8) ---@returns table<string, int> byte_idx and char_idx of first change position -local function align_position(line, byte, align, offset_encoding) +local function align_end_position(line, byte, offset_encoding) local char -- If on the first byte, or an empty string: the trivial case if byte == 1 or #line == 0 then char = byte -- Called in the case of extending an empty line "" -> "a" elseif byte == #line + 1 then - byte = byte + str_utf_end(line, #line) - char = byte_to_utf(line, byte, offset_encoding) + char = compute_line_length(line, offset_encoding) + 1 else -- Modifying line, find the nearest utf codepoint - if align == 'start' then - byte = byte + str_utf_start(line, byte) + local offset = str_utf_start(line, byte) + -- If the byte does not fall on the start of the character, then + -- align to the start of the next character. + if offset < 0 then + byte = byte + str_utf_end(line, byte) + 1 + end + if byte <= #line then char = byte_to_utf(line, byte, offset_encoding) - elseif align == 'end' then - local offset = str_utf_end(line, byte) - -- If the byte does not fall on the start of the character, then - -- align to the start of the next character. - if offset > 0 then - char = byte_to_utf(line, byte, offset_encoding) + 1 - byte = byte + offset - else - char = byte_to_utf(line, byte, offset_encoding) - byte = byte + offset - end else - error('`align` must be start or end.') + char = compute_line_length(line, offset_encoding) + 1 end -- Extending line, find the nearest utf codepoint for the last valid character end @@ -131,7 +135,7 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline, -- occur on a new line pointed to by lastline. This occurs during insertion of -- new lines(O), the new newline is inserted at the line indicated by -- new_lastline. - -- If firstline == new_lastline, the first change occured on a line that was deleted. + -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- In this case, the first byte change is also at the first byte of firstline if firstline == new_lastline or firstline == lastline then return { line_idx = firstline, byte_idx = 1, char_idx = 1 } @@ -154,7 +158,18 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline, end -- Convert byte to codepoint if applicable - local byte_idx, char_idx = align_position(prev_line, start_byte_idx, 'start', offset_encoding) + local char_idx + local byte_idx + if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1)then + byte_idx = start_byte_idx + char_idx = 1 + elseif start_byte_idx == #prev_line + 1 then + byte_idx = start_byte_idx + char_idx = compute_line_length(prev_line, offset_encoding) + 1 + else + byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx) + char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding) + end -- Return the start difference (shared for new and prev lines) return { line_idx = firstline, byte_idx = byte_idx, char_idx = char_idx } @@ -174,7 +189,7 @@ end ---@param offset_encoding string ---@returns (int, int) end_line_idx and end_col_idx of range local function compute_end_range(prev_lines, curr_lines, start_range, firstline, lastline, new_lastline, offset_encoding) - -- If firstline == new_lastline, the first change occured on a line that was deleted. + -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- In this case, the last_byte... if firstline == new_lastline then return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { line_idx = firstline, byte_idx = 1, char_idx = 1 } @@ -219,7 +234,12 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline, -- Iterate from end to beginning of shortest line local prev_end_byte_idx = prev_line_length - byte_offset + 1 - local prev_byte_idx, prev_char_idx = align_position(prev_line, prev_end_byte_idx, 'start', offset_encoding) + + -- Handle case where lines match + if prev_end_byte_idx == 0 then + prev_end_byte_idx = 1 + end + local prev_byte_idx, prev_char_idx = align_end_position(prev_line, prev_end_byte_idx, offset_encoding) local prev_end_range = { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx } local curr_end_range @@ -228,7 +248,11 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline, curr_end_range = { line_idx = start_line_idx, byte_idx = 1, char_idx = 1 } else local curr_end_byte_idx = curr_line_length - byte_offset + 1 - local curr_byte_idx, curr_char_idx = align_position(curr_line, curr_end_byte_idx, 'start', offset_encoding) + -- Handle case where lines match + if curr_end_byte_idx == 0 then + curr_end_byte_idx = 1 + end + local curr_byte_idx, curr_char_idx = align_end_position(curr_line, curr_end_byte_idx, offset_encoding) curr_end_range = { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx } end @@ -272,18 +296,6 @@ local function extract_text(lines, start_range, end_range, line_ending) end end -local function compute_line_length(line, offset_encoding) - local length - local _ - if offset_encoding == 'utf-16' then - _, length = str_utfindex(line) - elseif offset_encoding == 'utf-32' then - length, _ = str_utfindex(line) - else - length = #line - end - return length -end ---@private -- rangelength depends on the offset encoding -- bytes for utf-8 (clangd with extenion) diff --git a/runtime/lua/vim/lsp/tagfunc.lua b/runtime/lua/vim/lsp/tagfunc.lua new file mode 100644 index 0000000000..5c55e8559f --- /dev/null +++ b/runtime/lua/vim/lsp/tagfunc.lua @@ -0,0 +1,76 @@ +local lsp = vim.lsp +local util = vim.lsp.util + +---@private +local function mk_tag_item(name, range, uri, offset_encoding) + local bufnr = vim.uri_to_bufnr(uri) + -- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position + local byte = util._get_line_byte_from_position(bufnr, range.start, offset_encoding) + 1 + return { + name = name, + filename = vim.uri_to_fname(uri), + cmd = string.format('call cursor(%d, %d)|', range.start.line + 1, byte), + } +end + +---@private +local function query_definition(pattern) + local params = lsp.util.make_position_params() + local results_by_client, err = lsp.buf_request_sync(0, 'textDocument/definition', params, 1000) + if err then + return {} + end + local results = {} + local add = function(range, uri, offset_encoding) + table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding)) + end + for client_id, lsp_results in pairs(results_by_client) do + local client = lsp.get_client_by_id(client_id) + local result = lsp_results.result or {} + if result.range then -- Location + add(result.range, result.uri) + else -- Location[] or LocationLink[] + for _, item in pairs(result) do + if item.range then -- Location + add(item.range, item.uri, client.offset_encoding) + else -- LocationLink + add(item.targetSelectionRange, item.targetUri, client.offset_encoding) + end + end + end + end + return results +end + +---@private +local function query_workspace_symbols(pattern) + local results_by_client, err = lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000) + if err then + return {} + end + local results = {} + for client_id, symbols in pairs(results_by_client) do + local client = lsp.get_client_by_id(client_id) + for _, symbol in pairs(symbols.result or {}) do + local loc = symbol.location + local item = mk_tag_item(symbol.name, loc.range, loc.uri, client.offset_encoding) + item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown' + table.insert(results, item) + end + end + return results +end + +---@private +local function tagfunc(pattern, flags) + local matches + if string.match(flags, 'c') then + matches = query_definition(pattern) + else + matches = query_workspace_symbols(pattern) + end + -- fall back to tags if no matches + return #matches > 0 and matches or vim.NIL +end + +return tagfunc diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index a4b7b9922b..68a030d50b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -90,6 +90,42 @@ local function split_lines(value) return split(value, '\n', true) end +--- Convert byte index to `encoding` index. +--- Convenience wrapper around vim.str_utfindex +---@param line string line to be indexed +---@param index number byte index (utf-8), or `nil` for length +---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@return number `encoding` index of `index` in `line` +function M._str_utfindex_enc(line, index, encoding) + if encoding ~= 'utf-8' then + local col32, col16 = vim.str_utfindex(line, index) + if encoding == 'utf-32' then + return col32 + else + return col16 + end + else + return index + end +end + +--- Convert UTF index to `encoding` index. +--- Convenience wrapper around vim.str_byteindex +---Alternative to vim.str_byteindex that takes an encoding. +---@param line string line to be indexed +---@param index number UTF index +---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@return number byte (utf-8) index of `encoding` index `index` in `line` +function M._str_byteindex_enc(line, index, encoding) + if encoding ~= 'utf-8' then + return vim.str_byteindex(line, index, not encoding or encoding ~= 'utf-32') + else + return index + end +end + +local _str_utfindex_enc = M._str_utfindex_enc +local _str_byteindex_enc = M._str_byteindex_enc --- Replaces text in a range with new text. --- --- CAUTION: Changes in-place! @@ -148,8 +184,96 @@ local function sort_by_key(fn) end ---@private +--- Gets the zero-indexed lines from the given buffer. +--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events. +--- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI. +--- +---@param bufnr number bufnr to get the lines from +---@param rows number[] zero-indexed line numbers +---@return table<number string> a table mapping rows to lines +local function get_lines(bufnr, rows) + rows = type(rows) == "table" and rows or { rows } + + ---@private + local function buf_lines() + local lines = {} + for _, row in pairs(rows) do + lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1] + end + return lines + end + + local uri = vim.uri_from_bufnr(bufnr) + + -- load the buffer if this is not a file uri + -- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds. + if uri:sub(1, 4) ~= "file" then + vim.fn.bufload(bufnr) + return buf_lines() + end + + -- use loaded buffers if available + if vim.fn.bufloaded(bufnr) == 1 then + return buf_lines() + end + + local filename = api.nvim_buf_get_name(bufnr) + + -- get the data from the file + local fd = uv.fs_open(filename, "r", 438) + if not fd then return "" end + local stat = uv.fs_fstat(fd) + local data = uv.fs_read(fd, stat.size, 0) + uv.fs_close(fd) + + local lines = {} -- rows we need to retrieve + local need = 0 -- keep track of how many unique rows we need + for _, row in pairs(rows) do + if not lines[row] then + need = need + 1 + end + lines[row] = true + end + + local found = 0 + local lnum = 0 + + for line in string.gmatch(data, "([^\n]*)\n?") do + if lines[lnum] == true then + lines[lnum] = line + found = found + 1 + if found == need then break end + end + lnum = lnum + 1 + end + + -- change any lines we didn't find to the empty string + for i, line in pairs(lines) do + if line == true then + lines[i] = "" + end + end + return lines +end + + +---@private +--- Gets the zero-indexed line from the given buffer. +--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events. +--- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI. +--- +---@param bufnr number +---@param row number zero-indexed line number +---@return string the line at row in filename +local function get_line(bufnr, row) + return get_lines(bufnr, { row })[row] +end + + +---@private --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position --- Returns a zero-indexed column, since set_lines() does the conversion to +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 --- 1-indexed local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed @@ -158,31 +282,19 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- When on the first character, we can ignore the difference between byte and -- character if col > 0 then - if not api.nvim_buf_is_loaded(bufnr) then - vim.fn.bufload(bufnr) - end - - local line = position.line - local lines = api.nvim_buf_get_lines(bufnr, line, line + 1, false) - if #lines > 0 then - local ok, result - - if offset_encoding == "utf-16" or not offset_encoding then - ok, result = pcall(vim.str_byteindex, lines[1], col, true) - elseif offset_encoding == "utf-32" then - ok, result = pcall(vim.str_byteindex, lines[1], col, false) - end - - if ok then - return result - end - return math.min(#lines[1], col) + local line = get_line(bufnr, position.line) + local ok, result + ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding) + if ok then + return result end + return math.min(#line, col) end return col end --- Process and return progress reports from lsp server +---@private function M.get_progress_messages() local new_messages = {} @@ -244,8 +356,15 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits table list of `TextEdit` objects ---@param bufnr number Buffer id +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to encoding of first client of `bufnr` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit -function M.apply_text_edits(text_edits, bufnr) +function M.apply_text_edits(text_edits, bufnr, offset_encoding) + validate { + text_edits = { text_edits, 't', false }; + bufnr = { bufnr, 'number', false }; + offset_encoding = { offset_encoding, 'string', true }; + } + offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) if not next(text_edits) then return end if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) @@ -282,8 +401,7 @@ function M.apply_text_edits(text_edits, bufnr) -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. local has_eol_text_edit = false local max = vim.api.nvim_buf_line_count(bufnr) - -- TODO handle offset_encoding - local _, len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '') + local len = _str_utfindex_enc(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '', nil, offset_encoding) text_edits = vim.tbl_map(function(text_edit) if max <= text_edit.range.start.line then text_edit.range.start.line = max - 1 @@ -474,7 +592,7 @@ local function remove_unmatch_completion_items(items, prefix) end, items) end ---- Acording to LSP spec, if the client set `completionItemKind.valueSet`, +--- According to LSP spec, if the client set `completionItemKind.valueSet`, --- the client must handle it properly even if it receives a value outside the --- specification. --- @@ -574,7 +692,7 @@ function M.rename(old_fname, new_fname, opts) api.nvim_buf_delete(oldbuf, { force = true }) end - +---@private local function create_file(change) local opts = change.options or {} -- from spec: Overwrite wins over `ignoreIfExists` @@ -586,7 +704,7 @@ local function create_file(change) vim.fn.bufadd(fname) end - +---@private local function delete_file(change) local opts = change.options or {} local fname = vim.uri_to_fname(change.uri) @@ -880,6 +998,8 @@ function M.jump_to_location(location) local row = range.start.line local col = get_line_byte_from_position(0, range.start) api.nvim_win_set_cursor(0, {row + 1, col}) + -- Open folds under the cursor + vim.cmd("normal! zv") return true end @@ -1054,7 +1174,7 @@ function M.stylize_markdown(bufnr, contents, opts) markdown_lines[#stripped] = true end else - -- strip any emty lines or separators prior to this separator in actual markdown + -- strip any empty lines or separators prior to this separator in actual markdown if line:match("^---+$") then while markdown_lines[#stripped] and (stripped[#stripped]:match("^%s*$") or stripped[#stripped]:match("^---+$")) do markdown_lines[#stripped] = false @@ -1087,7 +1207,7 @@ function M.stylize_markdown(bufnr, contents, opts) local idx = 1 ---@private - -- keep track of syntaxes we already inlcuded. + -- keep track of syntaxes we already included. -- no need to include the same syntax more than once local langs = {} local fences = get_markdown_fences() @@ -1133,17 +1253,57 @@ function M.stylize_markdown(bufnr, contents, opts) return stripped end +---@private --- Creates autocommands to close a preview window when events happen. --- ----@param events (table) list of events ----@param winnr (number) window id of preview window +---@param events table list of events +---@param winnr number window id of preview window +---@param bufnrs table list of buffers where the preview window will remain visible ---@see |autocmd-events| -function M.close_preview_autocmd(events, winnr) +local function close_preview_autocmd(events, winnr, bufnrs) + local augroup = 'preview_window_'..winnr + + -- close the preview window when entered a buffer that is not + -- the floating window buffer or the buffer that spawned it + vim.cmd(string.format([[ + augroup %s + autocmd! + autocmd BufEnter * lua vim.lsp.util._close_preview_window(%d, {%s}) + augroup end + ]], augroup, winnr, table.concat(bufnrs, ','))) + if #events > 0 then - api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") + vim.cmd(string.format([[ + augroup %s + autocmd %s <buffer> lua vim.lsp.util._close_preview_window(%d) + augroup end + ]], augroup, table.concat(events, ','), winnr)) end end +---@private +--- Closes the preview window +--- +---@param winnr number window id of preview window +---@param bufnrs table|nil optional list of ignored buffers +function M._close_preview_window(winnr, bufnrs) + vim.schedule(function() + -- exit if we are in one of ignored buffers + if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then + return + end + + local augroup = 'preview_window_'..winnr + vim.cmd(string.format([[ + augroup %s + autocmd! + augroup end + augroup! %s + ]], augroup, augroup)) + pcall(vim.api.nvim_win_close, winnr, true) + end) +end + ---@internal --- Computes size of float needed to show contents (with optional wrapping) --- @@ -1223,18 +1383,21 @@ end --- ---@param contents table of lines to show in window ---@param syntax string of syntax to set for opened buffer ----@param opts dictionary with optional fields ---- - height of floating window ---- - width of floating window ---- - wrap boolean enable wrapping of long lines (defaults to true) ---- - wrap_at character to wrap at for computing height when wrap is enabled ---- - max_width maximal width of floating window ---- - max_height maximal height of floating window ---- - pad_top number of lines to pad contents at top ---- - pad_bottom number of lines to pad contents at bottom ---- - focus_id if a popup with this id is opened, then focus it ---- - close_events list of events that closes the floating window ---- - focusable (boolean, default true): Make float focusable +---@param opts table with optional fields (additional keys are passed on to |vim.api.nvim_open_win()|) +--- - height: (number) height of floating window +--- - width: (number) width of floating window +--- - wrap: (boolean, default true) wrap long lines +--- - wrap_at: (string) character to wrap at for computing height when wrap is enabled +--- - max_width: (number) maximal width of floating window +--- - max_height: (number) maximal height of floating window +--- - pad_top: (number) number of lines to pad contents at top +--- - pad_bottom: (number) number of lines to pad contents at bottom +--- - focus_id: (string) if a popup with this id is opened, then focus it +--- - close_events: (table) list of events that closes the floating window +--- - focusable: (boolean, default true) Make float focusable +--- - focus: (boolean, default true) If `true`, and if {focusable} +--- is also `true`, focus an existing floating window with the same +--- {focus_id} ---@returns bufnr,winnr buffer and window number of the newly created floating ---preview window function M.open_floating_preview(contents, syntax, opts) @@ -1246,12 +1409,13 @@ function M.open_floating_preview(contents, syntax, opts) opts = opts or {} opts.wrap = opts.wrap ~= false -- wrapping by default opts.stylize_markdown = opts.stylize_markdown ~= false - opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre"} + opts.focus = opts.focus ~= false + opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "InsertCharPre"} local bufnr = api.nvim_get_current_buf() -- check if this popup is focusable and we need to focus - if opts.focus_id and opts.focusable ~= false then + if opts.focus_id and opts.focusable ~= false and opts.focus then -- Go back to previous window if we are in a focusable one local current_winnr = api.nvim_get_current_win() if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then @@ -1314,8 +1478,8 @@ function M.open_floating_preview(contents, syntax, opts) api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') - api.nvim_buf_set_keymap(floating_bufnr, "n", "q", "<cmd>bdelete<cr>", {silent = true, noremap = true}) - M.close_preview_autocmd(opts.close_events, floating_winnr) + api.nvim_buf_set_keymap(floating_bufnr, "n", "q", "<cmd>bdelete<cr>", {silent = true, noremap = true, nowait = true}) + close_preview_autocmd(opts.close_events, floating_winnr, {floating_bufnr, bufnr}) -- save focus_id if opts.focus_id then @@ -1331,7 +1495,7 @@ do --[[ References ]] --- Removes document highlights from a buffer. --- - ---@param bufnr buffer id + ---@param bufnr number Buffer id function M.buf_clear_references(bufnr) validate { bufnr = {bufnr, 'n', true} } api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) @@ -1339,21 +1503,19 @@ do --[[ References ]] --- Shows a list of document highlights for a certain buffer. --- - ---@param bufnr buffer id - ---@param references List of `DocumentHighlight` objects to highlight + ---@param bufnr number Buffer id + ---@param references table List of `DocumentHighlight` objects to highlight + ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to `offset_encoding` of first client of `bufnr` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight - function M.buf_highlight_references(bufnr, references, client_id) + function M.buf_highlight_references(bufnr, references, offset_encoding) validate { bufnr = {bufnr, 'n', true} } - local client = vim.lsp.get_client_by_id(client_id) - if not client then - return - end + offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) for _, reference in ipairs(references) do local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"] local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"] - local start_idx = get_line_byte_from_position(bufnr, { line = start_line, character = start_char }, client.offset_encoding) - local end_idx = get_line_byte_from_position(bufnr, { line = start_line, character = end_char }, client.offset_encoding) + local start_idx = get_line_byte_from_position(bufnr, { line = start_line, character = start_char }, offset_encoding) + local end_idx = get_line_byte_from_position(bufnr, { line = start_line, character = end_char }, offset_encoding) local document_highlight_kind = { [protocol.DocumentHighlightKind.Text] = "LspReferenceText"; @@ -1374,88 +1536,6 @@ local position_sort = sort_by_key(function(v) return {v.start.line, v.start.character} end) ---- Gets the zero-indexed line from the given uri. ----@param uri string uri of the resource to get the line from ----@param row number zero-indexed line number ----@return string the line at row in filename --- For non-file uris, we load the buffer and get the line. --- If a loaded buffer exists, then that is used. --- Otherwise we get the line using libuv which is a lot faster than loading the buffer. -function M.get_line(uri, row) - return M.get_lines(uri, { row })[row] -end - ---- Gets the zero-indexed lines from the given uri. ----@param uri string uri of the resource to get the lines from ----@param rows number[] zero-indexed line numbers ----@return table<number string> a table mapping rows to lines --- For non-file uris, we load the buffer and get the lines. --- If a loaded buffer exists, then that is used. --- Otherwise we get the lines using libuv which is a lot faster than loading the buffer. -function M.get_lines(uri, rows) - rows = type(rows) == "table" and rows or { rows } - - local function buf_lines(bufnr) - local lines = {} - for _, row in pairs(rows) do - lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1] - end - return lines - end - - -- load the buffer if this is not a file uri - -- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds. - if uri:sub(1, 4) ~= "file" then - local bufnr = vim.uri_to_bufnr(uri) - vim.fn.bufload(bufnr) - return buf_lines(bufnr) - end - - local filename = vim.uri_to_fname(uri) - - -- use loaded buffers if available - if vim.fn.bufloaded(filename) == 1 then - local bufnr = vim.fn.bufnr(filename, false) - return buf_lines(bufnr) - end - - -- get the data from the file - local fd = uv.fs_open(filename, "r", 438) - if not fd then return "" end - local stat = uv.fs_fstat(fd) - local data = uv.fs_read(fd, stat.size, 0) - uv.fs_close(fd) - - local lines = {} -- rows we need to retrieve - local need = 0 -- keep track of how many unique rows we need - for _, row in pairs(rows) do - if not lines[row] then - need = need + 1 - end - lines[row] = true - end - - local found = 0 - local lnum = 0 - - for line in string.gmatch(data, "([^\n]*)\n?") do - if lines[lnum] == true then - lines[lnum] = line - found = found + 1 - if found == need then break end - end - lnum = lnum + 1 - end - - -- change any lines we didn't find to the empty string - for i, line in pairs(lines) do - if line == true then - lines[i] = "" - end - end - return lines -end - --- Returns the items with the byte position calculated correctly and in sorted --- order, for display in quickfix and location lists. --- @@ -1498,7 +1578,7 @@ function M.locations_to_items(locations) end -- get all the lines for this uri - local lines = M.get_lines(uri, uri_rows) + local lines = get_lines(vim.uri_to_bufnr(uri), uri_rows) for _, temp in ipairs(rows) do local pos = temp.start @@ -1524,6 +1604,7 @@ end --- ---@param items (table) list of items function M.set_loclist(items, win_id) + vim.api.nvim_echo({{'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) vim.fn.setloclist(win_id or 0, {}, ' ', { title = 'Language Server'; items = items; @@ -1537,13 +1618,14 @@ end --- ---@param items (table) list of items function M.set_qflist(items) + vim.api.nvim_echo({{'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) vim.fn.setqflist({}, ' ', { title = 'Language Server'; items = items; }) end --- Acording to LSP spec, if the client set "symbolKind.valueSet", +-- According to LSP spec, if the client set "symbolKind.valueSet", -- the client must handle it properly even if it receives a value outside the specification. -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol function M._get_symbol_kind_name(symbol_kind) @@ -1640,43 +1722,78 @@ function M.try_trim_markdown_code_blocks(lines) return 'markdown' end -local str_utfindex = vim.str_utfindex ---@private -local function make_position_param() - local row, col = unpack(api.nvim_win_get_cursor(0)) +---@param window (optional, number): window handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` +local function make_position_param(window, offset_encoding) + window = window or 0 + local buf = vim.api.nvim_win_get_buf(window) + local row, col = unpack(api.nvim_win_get_cursor(window)) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) row = row - 1 - local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] if not line then return { line = 0; character = 0; } end - -- TODO handle offset_encoding - local _ - _, col = str_utfindex(line, col) + + col = _str_utfindex_enc(line, col, offset_encoding) + return { line = row; character = col; } end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- +---@param window (optional, number): window handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams -function M.make_position_params() +function M.make_position_params(window, offset_encoding) + window = window or 0 + local buf = vim.api.nvim_win_get_buf(window) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) return { - textDocument = M.make_text_document_params(); - position = make_position_param() + textDocument = M.make_text_document_params(buf); + position = make_position_param(window, offset_encoding) } end +--- Utility function for getting the encoding of the first LSP client on the given buffer. +---@param bufnr (number) buffer handle or 0 for current, defaults to current +---@returns (string) encoding first client if there is one, nil otherwise +function M._get_offset_encoding(bufnr) + validate { + bufnr = {bufnr, 'n', true}; + } + + local offset_encoding + + for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do + local this_offset_encoding = client.offset_encoding or "utf-16" + if not offset_encoding then + offset_encoding = this_offset_encoding + elseif offset_encoding ~= this_offset_encoding then + vim.notify("warning: multiple different client offset_encodings detected for buffer, this is not supported yet", vim.log.levels.WARN) + end + end + + return offset_encoding +end + --- Using the current position in the current buffer, creates an object that --- can be used as a building block for several LSP requests, such as --- `textDocument/codeAction`, `textDocument/colorPresentation`, --- `textDocument/rangeFormatting`. --- +---@param window (optional, number): window handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } -function M.make_range_params() - local position = make_position_param() +function M.make_range_params(window, offset_encoding) + local buf = vim.api.nvim_win_get_buf(window) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) + local position = make_position_param(window, offset_encoding) return { - textDocument = M.make_text_document_params(), + textDocument = M.make_text_document_params(buf), range = { start = position; ["end"] = position; } } end @@ -1688,27 +1805,29 @@ end ---Defaults to the start of the last visual selection. ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. +---@param bufnr (optional, number): buffer handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `bufnr` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } -function M.make_given_range_params(start_pos, end_pos) +function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate { start_pos = {start_pos, 't', true}; end_pos = {end_pos, 't', true}; + offset_encoding = {offset_encoding, 's', true}; } - local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<')) - local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>')) + bufnr = bufnr or 0 + offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) + local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) + local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) -- convert to 0-index A[1] = A[1] - 1 B[1] = B[1] - 1 - -- account for encoding. - -- TODO handle offset_encoding + -- account for offset_encoding. if A[2] > 0 then - local _, char = M.character_offset(0, A[1], A[2]) - A = {A[1], char} + A = {A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding)} end if B[2] > 0 then - local _, char = M.character_offset(0, B[1], B[2]) - B = {B[1], char} + B = {B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding)} end -- we need to offset the end character position otherwise we loose the last -- character of the selection, as LSP end position is exclusive @@ -1717,7 +1836,7 @@ function M.make_given_range_params(start_pos, end_pos) B[2] = B[2] + 1 end return { - textDocument = M.make_text_document_params(), + textDocument = M.make_text_document_params(bufnr), range = { start = {line = A[1], character = A[2]}, ['end'] = {line = B[1], character = B[2]} @@ -1727,10 +1846,11 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- +---@param bufnr (optional, number): Buffer handle, defaults to current ---@returns `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier -function M.make_text_document_params() - return { uri = vim.uri_from_bufnr(0) } +function M.make_text_document_params(bufnr) + return { uri = vim.uri_from_bufnr(bufnr or 0) } end --- Create the workspace params @@ -1773,15 +1893,16 @@ end ---@param buf buffer id (0 for current) ---@param row 0-indexed line ---@param col 0-indexed byte offset in line ----@returns (number, number) UTF-32 and UTF-16 index of the character in line {row} column {col} in buffer {buf} -function M.character_offset(bufnr, row, col) - local uri = vim.uri_from_bufnr(bufnr) - local line = M.get_line(uri, row) +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf` +---@returns (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf} +function M.character_offset(buf, row, col, offset_encoding) + local line = get_line(buf, row) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) -- If the col is past the EOL, use the line length. if col > #line then - return str_utfindex(line) + return _str_utfindex_enc(line, nil, offset_encoding) end - return str_utfindex(line, col) + return _str_utfindex_enc(line, col, offset_encoding) end --- Helper function to return nested values in language server settings diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 6e40b6ca52..1cf618725d 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -12,7 +12,7 @@ local vim = vim or {} --- same functions as those in the input table. Userdata and threads are not --- copied and will throw an error. --- ----@param orig Table to copy +---@param orig table Table to copy ---@returns New table of copied keys and (nested) values. function vim.deepcopy(orig) end -- luacheck: no unused vim.deepcopy = (function() @@ -21,17 +21,16 @@ vim.deepcopy = (function() end local deepcopy_funcs = { - table = function(orig) + table = function(orig, cache) + if cache[orig] then return cache[orig] end local copy = {} - if vim._empty_dict_mt ~= nil and getmetatable(orig) == vim._empty_dict_mt then - copy = vim.empty_dict() - end - + cache[orig] = copy + local mt = getmetatable(orig) for k, v in pairs(orig) do - copy[vim.deepcopy(k)] = vim.deepcopy(v) + copy[vim.deepcopy(k, cache)] = vim.deepcopy(v, cache) end - return copy + return setmetatable(copy, mt) end, number = _id, string = _id, @@ -40,10 +39,10 @@ vim.deepcopy = (function() ['function'] = _id, } - return function(orig) + return function(orig, cache) local f = deepcopy_funcs[type(orig)] if f then - return f(orig) + return f(orig, cache or {}) else error("Cannot deepcopy object of type "..type(orig)) end @@ -560,6 +559,7 @@ do return type(val) == t or (t == 'callable' and vim.is_callable(val)) end + ---@private local function is_valid(opt) if type(opt) ~= 'table' then return false, string.format('opt: expected table, got %s', type(opt)) diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 66999c5f7f..07f6418c0c 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -72,7 +72,7 @@ end --- Gets the parser for this bufnr / ft combination. --- --- If needed this will create the parser. ---- Unconditionnally attach the provided callback +--- Unconditionally attach the provided callback --- ---@param bufnr The buffer the parser should be tied to ---@param lang The filetype of this parser diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 89ddd6cd5a..6f347ff25f 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -38,7 +38,7 @@ end --- Inspects the provided language. --- ---- Inspecting provides some useful informations on the language like node names, ... +--- Inspecting provides some useful information on the language like node names, ... --- ---@param lang The language. function M.inspect_language(lang) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 7e392f72a4..594765761d 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -493,9 +493,9 @@ local function tree_contains(tree, range) return false end ---- Determines wether @param range is contained in this language tree +--- Determines whether @param range is contained in this language tree --- ---- This goes down the tree to recursively check childs. +--- This goes down the tree to recursively check children. --- ---@param range A range, that is a `{ start_line, start_col, end_line, end_col }` table. function LanguageTree:contains(range) diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 66da179ea3..ebed502c92 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -48,7 +48,7 @@ function M.get_query_files(lang, query_name, is_included) local base_langs = {} -- Now get the base languages by looking at the first line of every file - -- The syntax is the folowing : + -- The syntax is the following : -- ;+ inherits: ({language},)*{language} -- -- {language} ::= {lang} | ({lang}) @@ -164,22 +164,36 @@ function M.parse_query(lang, query) return self end --- TODO(vigoux): support multiline nodes too - --- Gets the text corresponding to a given node --- ---@param node the node ----@param bsource The buffer or string from which the node is extracted +---@param source The buffer or string from which the node is extracted function M.get_node_text(node, source) local start_row, start_col, start_byte = node:start() local end_row, end_col, end_byte = node:end_() if type(source) == "number" then - if start_row ~= end_row then + local lines + local eof_row = a.nvim_buf_line_count(source) + if start_row >= eof_row then return nil end - local line = a.nvim_buf_get_lines(source, start_row, start_row+1, true)[1] - return string.sub(line, start_col+1, end_col) + + if end_col == 0 then + lines = a.nvim_buf_get_lines(source, start_row, end_row, true) + end_col = -1 + else + lines = a.nvim_buf_get_lines(source, start_row, end_row + 1, true) + end + + if #lines == 1 then + lines[1] = string.sub(lines[1], start_col+1, end_col) + else + lines[1] = string.sub(lines[1], start_col+1) + lines[#lines] = string.sub(lines[#lines], 1, end_col) + end + + return table.concat(lines, "\n") elseif type(source) == "string" then return source:sub(start_byte+1, end_byte) end @@ -211,11 +225,6 @@ local predicate_handlers = { ["lua-match?"] = function(match, _, source, predicate) local node = match[predicate[2]] local regex = predicate[3] - local start_row, _, end_row, _ = node:range() - if start_row ~= end_row then - return false - end - return string.find(M.get_node_text(node, source), regex) end, @@ -239,13 +248,8 @@ local predicate_handlers = { return function(match, _, source, pred) local node = match[pred[2]] - local start_row, start_col, end_row, end_col = node:range() - if start_row ~= end_row then - return false - end - local regex = compiled_vim_regexes[pred[3]] - return regex:match_line(source, start_row, start_col, end_col) + return regex:match_str(M.get_node_text(node, source)) end end)(), @@ -446,7 +450,7 @@ end --- --- {source} is needed if the query contains predicates, then the caller --- must ensure to use a freshly parsed tree consistent with the current ---- text of the buffer (if relevent). {start_row} and {end_row} can be used to limit +--- text of the buffer (if relevant). {start_row} and {end_row} can be used to limit --- matches inside a row range (this is typically used with root node --- as the node, i e to get syntax highlight matches in the current --- viewport). When omitted the start and end row values are used from the given node. @@ -466,7 +470,7 @@ end --- </pre> --- ---@param node The node under which the search will occur ----@param source The source buffer or string to exctract text from +---@param source The source buffer or string to extract text from ---@param start The starting line of the search ---@param stop The stopping line of the search (end-exclusive) --- diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index 5d8d4fa169..d08d2a3ee3 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -56,8 +56,8 @@ local function is_windows_file_uri(uri) end --- Get a URI from a file path. ----@param path (string): Path to file ----@return URI +---@param path string Path to file +---@return string URI local function uri_from_fname(path) local volume_path, fname = path:match("^([a-zA-Z]:)(.*)") local is_windows = volume_path ~= nil @@ -78,8 +78,8 @@ local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):.*' local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):[a-zA-Z]:.*' --- Get a URI from a bufnr ----@param bufnr (number): Buffer number ----@return URI +---@param bufnr number +---@return string URI local function uri_from_bufnr(bufnr) local fname = vim.api.nvim_buf_get_name(bufnr) local volume_path = fname:match("^([a-zA-Z]:).*") @@ -99,8 +99,8 @@ local function uri_from_bufnr(bufnr) end --- Get a filename from a URI ----@param uri (string): The URI ----@return Filename +---@param uri string +---@return string filename or unchanged URI for non-file URIs local function uri_to_fname(uri) local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) if scheme ~= 'file' then @@ -117,17 +117,13 @@ local function uri_to_fname(uri) return uri end ---- Return or create a buffer for a uri. ----@param uri (string): The URI ----@return bufnr. ----@note Creates buffer but does not load it +--- Get the buffer for a uri. +--- Creates a new unloaded buffer if no buffer for the uri already exists. +-- +---@param uri string +---@return number bufnr local function uri_to_bufnr(uri) - local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) - if scheme == 'file' then - return vim.fn.bufadd(uri_to_fname(uri)) - else - return vim.fn.bufadd(uri) - end + return vim.fn.bufadd(uri_to_fname(uri)) end return { diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index 099c9a57c0..225dd79878 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ </screenshots> <releases> + <release date="2021-11-30" version="0.6.0"/> <release date="2021-07-02" version="0.5.0"/> <release date="2020-08-04" version="0.4.4"/> <release date="2019-11-06" version="0.4.3"/> diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt index 58a47780ef..b719fae730 100644 --- a/runtime/pack/dist/opt/matchit/doc/matchit.txt +++ b/runtime/pack/dist/opt/matchit/doc/matchit.txt @@ -1,10 +1,10 @@ -*matchit.txt* Extended "%" matching +*matchit.txt* Extended "%" matching For instructions on installing this file, type `:help matchit-install` inside Vim. -For Vim version 8.1. Last change: 2021 May 17 +For Vim version 8.1. Last change: 2021 Nov 13 VIM REFERENCE MANUAL by Benji Fisher et al diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 8de03dfab7..8fa556aab6 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2021 Oct 26 +" Last Change: 2021 Dec 16 " " WORK IN PROGRESS - Only the basics work " Note: On MS-Windows you need a recent version of gdb. The one included with @@ -102,6 +102,7 @@ endfunc call s:Highlight(1, '', &background) hi default debugBreakpoint term=reverse ctermbg=red guibg=red +hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray func s:StartDebug(bang, ...) " First argument is the command to debug, second core file or process ID. @@ -241,15 +242,29 @@ func s:StartDebug_term(dict) let comm_job_info = nvim_get_chan_info(s:comm_job_id) let commpty = comm_job_info['pty'] - " Open a terminal window to run the debugger. - " Add -quiet to avoid the intro message causing a hit-enter prompt. let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) - let cmd = [g:termdebugger, '-quiet', '-tty', pty, '--eval-command', 'echo startupdone\n'] + gdb_args - "call ch_log('executing "' . join(cmd) . '"') + let gdb_cmd = [g:termdebugger] + " Add -quiet to avoid the intro message causing a hit-enter prompt. + let gdb_cmd += ['-quiet'] + " Disable pagination, it causes everything to stop at the gdb + let gdb_cmd += ['-iex', 'set pagination off'] + " Interpret commands while the target is running. This should usually only + " be exec-interrupt, since many commands don't work properly while the + " target is running (so execute during startup). + let gdb_cmd += ['-iex', 'set mi-async on'] + " Open a terminal window to run the debugger. + let gdb_cmd += ['-tty', pty] + " Command executed _after_ startup is done, provides us with the necessary feedback + let gdb_cmd += ['-ex', 'echo startupdone\n'] + + " Adding arguments requested by the user + let gdb_cmd += gdb_args + execute 'new' - let s:gdb_job_id = termopen(cmd, {'on_exit': function('s:EndTermDebug')}) + " call ch_log('executing "' . join(gdb_cmd) . '"') + let s:gdb_job_id = termopen(gdb_cmd, {'on_exit': function('s:EndTermDebug')}) if s:gdb_job_id == 0 echoerr 'invalid argument (or job table is full) while opening gdb terminal window' exe 'bwipe! ' . s:ptybuf @@ -272,8 +287,8 @@ func s:StartDebug_term(dict) for lnum in range(1, 200) if get(getbufline(s:gdbbuf, lnum), 0, '') =~ 'startupdone' - let try_count = 9999 - break + let try_count = 9999 + break endif endfor let try_count += 1 @@ -286,11 +301,12 @@ func s:StartDebug_term(dict) " Set arguments to be run. if len(proc_args) - call chansend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") + call chansend(s:gdb_job_id, 'server set args ' . join(proc_args) . "\r") endif - " Connect gdb to the communication pty, using the GDB/MI interface - call chansend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r") + " Connect gdb to the communication pty, using the GDB/MI interface. + " Prefix "server" to avoid adding this to the history. + call chansend(s:gdb_job_id, 'server new-ui mi ' . commpty . "\r") " Wait for the response to show up, users may not notice the error and wonder " why the debugger doesn't work. @@ -309,7 +325,8 @@ func s:StartDebug_term(dict) let response = line1 . line2 if response =~ 'Undefined command' echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' - call s:CloseBuffers() + " CHECKME: possibly send a "server show version" here + call s:CloseBuffers() return endif if response =~ 'New UI allocated' @@ -332,17 +349,6 @@ func s:StartDebug_term(dict) sleep 10m endwhile - " Interpret commands while the target is running. This should usually only be - " exec-interrupt, since many commands don't work properly while the target is - " running. - call s:SendCommand('-gdb-set mi-async on') - " Older gdb uses a different command. - call s:SendCommand('-gdb-set target-async on') - - " Disable pagination, it causes everything to stop at the gdb - " "Type <return> to continue" prompt. - call s:SendCommand('set pagination off') - " Set the filetype, this can be used to add mappings. set filetype=termdebug @@ -370,14 +376,26 @@ func s:StartDebug_prompt(dict) exe (&columns / 2 - 1) . "wincmd |" endif - " Add -quiet to avoid the intro message causing a hit-enter prompt. let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) - let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args - "call ch_log('executing "' . join(cmd) . '"') + let gdb_cmd = [g:termdebugger] + " Add -quiet to avoid the intro message causing a hit-enter prompt. + let gdb_cmd += ['-quiet'] + " Disable pagination, it causes everything to stop at the gdb, needs to be run early + let gdb_cmd += ['-iex', 'set pagination off'] + " Interpret commands while the target is running. This should usually only + " be exec-interrupt, since many commands don't work properly while the + " target is running (so execute during startup). + let gdb_cmd += ['-iex', 'set mi-async on'] + " directly communicate via mi2 + let gdb_cmd += ['--interpreter=mi2'] - let s:gdbjob = jobstart(cmd, { + " Adding arguments requested by the user + let gdb_cmd += gdb_args + + " call ch_log('executing "' . join(gdb_cmd) . '"') + let s:gdbjob = jobstart(gdb_cmd, { \ 'on_exit': function('s:EndPromptDebug'), \ 'on_stdout': function('s:GdbOutCallback'), \ }) @@ -391,13 +409,6 @@ func s:StartDebug_prompt(dict) return endif - " Interpret commands while the target is running. This should usually only - " be exec-interrupt, since many commands don't work properly while the - " target is running. - call s:SendCommand('-gdb-set mi-async on') - " Older gdb uses a different command. - call s:SendCommand('-gdb-set target-async on') - let s:ptybuf = 0 if has('win32') " MS-Windows: run in a new console window for maximum compatibility @@ -432,8 +443,6 @@ func s:StartDebug_prompt(dict) endif call s:SendCommand('set print pretty on') call s:SendCommand('set breakpoint pending on') - " Disable pagination, it causes everything to stop at the gdb - call s:SendCommand('set pagination off') " Set arguments to be run if len(proc_args) @@ -476,7 +485,7 @@ func s:StartDebugCommon(dict) " Run the command if the bang attribute was given and got to the debug " window. if get(a:dict, 'bang', 0) - call s:SendCommand('-exec-run') + call s:SendResumingCommand('-exec-run') call win_gotoid(s:ptywin) endif endfunc @@ -504,7 +513,7 @@ func TermDebugSendCommand(cmd) " needed once. call jobstop(s:gdbjob) else - call s:SendCommand('-exec-interrupt') + Stop endif sleep 10m endif @@ -515,6 +524,20 @@ func TermDebugSendCommand(cmd) endif endfunc +" Send a command that resumes the program. If the program isn't stopped the +" command is not sent (to avoid a repeated command to cause trouble). +" If the command is sent then reset s:stopped. +func s:SendResumingCommand(cmd) + if s:stopped + " reset s:stopped here, it may take a bit of time before we get a response + let s:stopped = 0 + " call ch_log('assume that program is running after this command') + call s:SendCommand(a:cmd) + " else + " call ch_log('dropping command, program is running: ' . a:cmd) + endif +endfunc + " Function called when entering a line in the prompt buffer. func s:PromptCallback(text) call s:SendCommand(a:text) @@ -583,33 +606,33 @@ func s:GdbOutCallback(job_id, msgs, event) endfunc " Decode a message from gdb. quotedText starts with a ", return the text up -" to the next ", unescaping characters. +" to the next ", unescaping characters: +" - remove line breaks +" - change \\t to \t +" - change \0xhh to \xhh (disabled for now) +" - change \ooo to octal +" - change \\ to \ func s:DecodeMessage(quotedText) if a:quotedText[0] != '"' echoerr 'DecodeMessage(): missing quote in ' . a:quotedText return endif - let result = '' - let i = 1 - while a:quotedText[i] != '"' && i < len(a:quotedText) - if a:quotedText[i] == '\' - let i += 1 - if a:quotedText[i] == 'n' - " drop \n - let i += 1 - continue - elseif a:quotedText[i] == 't' - " append \t - let i += 1 - let result .= "\t" - continue - endif - endif - let result .= a:quotedText[i] - let i += 1 - endwhile - return result + return a:quotedText + \ ->substitute('^"\|".*\|\\n', '', 'g') + \ ->substitute('\\t', "\t", 'g') + " multi-byte characters arrive in octal form + " NULL-values must be kept encoded as those break the string otherwise + \ ->substitute('\\000', s:NullRepl, 'g') + \ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') + " Note: GDB docs also mention hex encodings - the translations below work + " but we keep them out for performance-reasons until we actually see + " those in mi-returns + " \ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') + " \ ->substitute('\\0x00', s:NullRepl, 'g') + \ ->substitute('\\\\', '\', 'g') + \ ->substitute(s:NullRepl, '\\000', 'g') endfunc +const s:NullRepl = 'XXXNULLXXX' " Extract the "name" value from a gdb message with fullname="name". func s:GetFullname(msg) @@ -657,8 +680,8 @@ func s:EndDebugCommon() if bufexists(bufnr) exe bufnr .. "buf" if exists('b:save_signcolumn') - let &signcolumn = b:save_signcolumn - unlet b:save_signcolumn + let &signcolumn = b:save_signcolumn + unlet b:save_signcolumn endif endif endfor @@ -770,7 +793,9 @@ func s:CommOutput(job_id, msgs, event) if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' call s:HandleCursor(msg) elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' - call s:HandleNewBreakpoint(msg) + call s:HandleNewBreakpoint(msg, 0) + elseif msg =~ '^=breakpoint-modified,' + call s:HandleNewBreakpoint(msg, 1) elseif msg =~ '^=breakpoint-deleted,' call s:HandleBreakpointDelete(msg) elseif msg =~ '^=thread-group-started' @@ -804,17 +829,20 @@ func s:InstallCommands() command -nargs=? Break call s:SetBreakpoint(<q-args>) command Clear call s:ClearBreakpoint() - command Step call s:SendCommand('-exec-step') - command Over call s:SendCommand('-exec-next') - command Finish call s:SendCommand('-exec-finish') + command Step call s:SendResumingCommand('-exec-step') + command Over call s:SendResumingCommand('-exec-next') + command Finish call s:SendResumingCommand('-exec-finish') command -nargs=* Run call s:Run(<q-args>) - command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) - command Stop call s:SendCommand('-exec-interrupt') + command -nargs=* Arguments call s:SendResumingCommand('-exec-arguments ' . <q-args>) - " using -exec-continue results in CTRL-C in gdb window not working if s:way == 'prompt' + command Stop call s:PromptInterrupt() command Continue call s:SendCommand('continue') else + command Stop call s:SendCommand('-exec-interrupt') + " using -exec-continue results in CTRL-C in the gdb window not working, + " communicating via commbuf (= use of SendCommand) has the same result + "command Continue call s:SendCommand('-exec-continue') command Continue call chansend(s:gdb_job_id, "continue\r") endif @@ -913,20 +941,16 @@ func s:SetBreakpoint(at) let do_continue = 0 if !s:stopped let do_continue = 1 - if s:way == 'prompt' - call s:PromptInterrupt() - else - call s:SendCommand('-exec-interrupt') - endif + Stop sleep 10m endif " Use the fname:lnum format, older gdb can't handle --source. let at = empty(a:at) ? - \ fnameescape(expand('%:p')) . ':' . line('.') : a:at + \ fnameescape(expand('%:p')) . ':' . line('.') : a:at call s:SendCommand('-break-insert ' . at) if do_continue - call s:SendCommand('-exec-continue') + Continue endif endfunc @@ -937,72 +961,112 @@ func s:ClearBreakpoint() let bploc = printf('%s:%d', fname, lnum) if has_key(s:breakpoint_locations, bploc) let idx = 0 + let nr = 0 for id in s:breakpoint_locations[bploc] if has_key(s:breakpoints, id) - " Assume this always works, the reply is simply "^done". - call s:SendCommand('-break-delete ' . id) - for subid in keys(s:breakpoints[id]) - exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) - endfor - unlet s:breakpoints[id] - unlet s:breakpoint_locations[bploc][idx] - break + " Assume this always works, the reply is simply "^done". + call s:SendCommand('-break-delete ' . id) + for subid in keys(s:breakpoints[id]) + exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) + endfor + unlet s:breakpoints[id] + unlet s:breakpoint_locations[bploc][idx] + let nr = id + break else - let idx += 1 + let idx += 1 endif endfor - if empty(s:breakpoint_locations[bploc]) - unlet s:breakpoint_locations[bploc] + if nr != 0 + if empty(s:breakpoint_locations[bploc]) + unlet s:breakpoint_locations[bploc] + endif + echomsg 'Breakpoint ' . id . ' cleared from line ' . lnum . '.' + else + echoerr 'Internal error trying to remove breakpoint at line ' . lnum . '!' endif + else + echomsg 'No breakpoint to remove at line ' . lnum . '.' endif endfunc func s:Run(args) if a:args != '' - call s:SendCommand('-exec-arguments ' . a:args) + call s:SendResumingCommand('-exec-arguments ' . a:args) endif - call s:SendCommand('-exec-run') + call s:SendResumingCommand('-exec-run') endfunc func s:SendEval(expr) - " clean up expression that may got in because of range - " (newlines and surrounding spaces) - let expr = a:expr - if &filetype ==# 'cobol' - " extra cleanup for COBOL: _every: expression ends with a period, - " a trailing comma is ignored as it commonly separates multiple expr. - let expr = substitute(expr, '\..*', '', '') - let expr = substitute(expr, '[;\n]', ' ', 'g') - let expr = substitute(expr, ',*$', '', '') + " check for "likely" boolean expressions, in which case we take it as lhs + if a:expr =~ "[=!<>]=" + let exprLHS = a:expr else - let expr = substitute(expr, '\n', ' ', 'g') + " remove text that is likely an assignment + let exprLHS = substitute(a:expr, ' *=.*', '', '') endif - let expr = substitute(expr, '^ *\(.*\) *', '\1', '') + " encoding expression to prevent bad errors + let expr = a:expr + let expr = substitute(expr, '\\', '\\\\', 'g') + let expr = substitute(expr, '"', '\\"', 'g') call s:SendCommand('-data-evaluate-expression "' . expr . '"') - let s:evalexpr = expr + let s:evalexpr = exprLHS endfunc -" :Evaluate - evaluate what is under the cursor +" :Evaluate - evaluate what is specified / under the cursor func s:Evaluate(range, arg) + let expr = s:GetEvaluationExpression(a:range, a:arg) + let s:ignoreEvalError = 0 + call s:SendEval(expr) +endfunc + +" get what is specified / under the cursor +func s:GetEvaluationExpression(range, arg) if a:arg != '' - let expr = a:arg - let s:evalFromBalloonExpr = 0 + " user supplied evaluation + let expr = s:CleanupExpr(a:arg) + " DSW: replace "likely copy + paste" assignment + let expr = substitute(expr, '"\([^"]*\)": *', '\1=', 'g') elseif a:range == 2 let pos = getcurpos() let reg = getreg('v', 1, 1) let regt = getregtype('v') normal! gv"vy - let expr = @v + let expr = s:CleanupExpr(@v) call setpos('.', pos) call setreg('v', reg, regt) let s:evalFromBalloonExpr = 1 else + " no evaluation provided: get from C-expression under cursor + " TODO: allow filetype specific lookup #9057 let expr = expand('<cexpr>') let s:evalFromBalloonExpr = 1 endif - let s:ignoreEvalError = 0 - call s:SendEval(expr) + return expr +endfunc + +" clean up expression that may got in because of range +" (newlines and surrounding whitespace) +" As it can also be specified via ex-command for assignments this function +" may not change the "content" parts (like replacing contained spaces +func s:CleanupExpr(expr) + " replace all embedded newlines/tabs/... + let expr = substitute(a:expr, '\_s', ' ', 'g') + + if &filetype ==# 'cobol' + " extra cleanup for COBOL: + " - a semicolon nmay be used instead of a space + " - a trailing comma or period is ignored as it commonly separates/ends + " multiple expr + let expr = substitute(expr, ';', ' ', 'g') + let expr = substitute(expr, '[,.]\+ *$', '', '') + endif + + " get rid of leading and trailing spaces + let expr = substitute(expr, '^ *', '', '') + let expr = substitute(expr, ' *$', '', '') + return expr endfunc let s:ignoreEvalError = 0 @@ -1011,9 +1075,20 @@ let s:evalFromBalloonExprResult = '' " Handle the result of data-evaluate-expression func s:HandleEvaluate(msg) - let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') - let value = substitute(value, '\\"', '"', 'g') - let value = substitute(value, '
', '\1', '') + let value = a:msg + \ ->substitute('.*value="\(.*\)"', '\1', '') + \ ->substitute('\\"', '"', 'g') + \ ->substitute('\\\\', '\\', 'g') + "\ multi-byte characters arrive in octal form, replace everthing but NULL values + \ ->substitute('\\000', s:NullRepl, 'g') + \ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') + "\ Note: GDB docs also mention hex encodings - the translations below work + "\ but we keep them out for performance-reasons until we actually see + "\ those in mi-returns + "\ ->substitute('\\0x00', s:NullRep, 'g') + "\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') + \ ->substitute(s:NullRepl, '\\000', 'g') + \ ->substitute('
', '\1', '') if s:evalFromBalloonExpr if s:evalFromBalloonExprResult == '' let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value @@ -1191,6 +1266,7 @@ func s:GotoAsmwinOrCreateIt() setlocal number setlocal noswapfile setlocal buftype=nofile + setlocal modifiable let asmbuf = bufnr('Termdebug-asm-listing') if asmbuf > 0 @@ -1245,15 +1321,15 @@ func s:HandleCursor(msg) let curwinid = win_getid(winnr()) if win_gotoid(s:asmwin) - let lnum = search('^' . s:asm_addr) - if lnum == 0 - call s:SendCommand('disassemble $pc') - else - exe 'sign unplace ' . s:asm_id - exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' - endif + let lnum = search('^' . s:asm_addr) + if lnum == 0 + call s:SendCommand('disassemble $pc') + else + exe 'sign unplace ' . s:asm_id + exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' + endif - call win_gotoid(curwinid) + call win_gotoid(curwinid) endif endif endif @@ -1273,11 +1349,12 @@ func s:HandleCursor(msg) endif endif exe lnum + normal! zv exe 'sign unplace ' . s:pc_id exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname if !exists('b:save_signcolumn') - let b:save_signcolumn = &signcolumn - call add(s:signcolumn_buflist, bufnr()) + let b:save_signcolumn = &signcolumn + call add(s:signcolumn_buflist, bufnr()) endif setlocal signcolumn=yes endif @@ -1290,11 +1367,16 @@ endfunc let s:BreakpointSigns = [] -func s:CreateBreakpoint(id, subid) +func s:CreateBreakpoint(id, subid, enabled) let nr = printf('%d.%d', a:id, a:subid) if index(s:BreakpointSigns, nr) == -1 call add(s:BreakpointSigns, nr) - exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" + if a:enabled == "n" + let hiName = "debugBreakpointDisabled" + else + let hiName = "debugBreakpoint" + endif + exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=" . hiName endif endfunc @@ -1304,9 +1386,14 @@ endfunction " Handle setting a breakpoint " Will update the sign that shows the breakpoint -func s:HandleNewBreakpoint(msg) +func s:HandleNewBreakpoint(msg, modifiedFlag) if a:msg !~ 'fullname=' - " a watch does not have a file name + " a watch or a pending breakpoint does not have a file name + if a:msg =~ 'pending=' + let nr = substitute(a:msg, '.*number=\"\([0-9.]*\)\".*', '\1', '') + let target = substitute(a:msg, '.*pending=\"\([^"]*\)\".*', '\1', '') + echomsg 'Breakpoint ' . nr . ' (' . target . ') pending.' + endif return endif for msg in s:SplitMsg(a:msg) @@ -1322,7 +1409,8 @@ func s:HandleNewBreakpoint(msg) " If "nr" is 123 it becomes "123.0" and subid is "0". " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') - call s:CreateBreakpoint(id, subid) + let enabled = substitute(msg, '.*enabled="\([yn]\)".*', '\1', '') + call s:CreateBreakpoint(id, subid, enabled) if has_key(s:breakpoints, id) let entries = s:breakpoints[id] @@ -1349,7 +1437,18 @@ func s:HandleNewBreakpoint(msg) if bufloaded(fname) call s:PlaceSign(id, subid, entry) + let posMsg = ' at line ' . lnum . '.' + else + let posMsg = ' in ' . fname . ' at line ' . lnum . '.' + endif + if !a:modifiedFlag + let actionTaken = 'created' + elseif enabled == 'n' + let actionTaken = 'disabled' + else + let actionTaken = 'enabled' endif + echomsg 'Breakpoint ' . nr . ' ' . actionTaken . posMsg endfor endfunc @@ -1369,11 +1468,12 @@ func s:HandleBreakpointDelete(msg) if has_key(s:breakpoints, id) for [subid, entry] in items(s:breakpoints[id]) if has_key(entry, 'placed') - exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) - unlet entry['placed'] + exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) + unlet entry['placed'] endif endfor unlet s:breakpoints[id] + echomsg 'Breakpoint ' . id . ' cleared.' endif endfunc @@ -1394,7 +1494,7 @@ func s:BufRead() for [id, entries] in items(s:breakpoints) for [subid, entry] in items(entries) if entry['fname'] == fname - call s:PlaceSign(id, subid, entry) + call s:PlaceSign(id, subid, entry) endif endfor endfor @@ -1406,7 +1506,7 @@ func s:BufUnloaded() for [id, entries] in items(s:breakpoints) for [subid, entry] in items(entries) if entry['fname'] == fname - let entry['placed'] = 0 + let entry['placed'] = 0 endif endfor endfor diff --git a/runtime/plugin/zipPlugin.vim b/runtime/plugin/zipPlugin.vim index b9e334fb98..edc52713a8 100644 --- a/runtime/plugin/zipPlugin.vim +++ b/runtime/plugin/zipPlugin.vim @@ -20,7 +20,7 @@ if &cp || exists("g:loaded_zipPlugin") finish endif -let g:loaded_zipPlugin = "v31" +let g:loaded_zipPlugin = "v32" let s:keepcpo = &cpo set cpo&vim diff --git a/runtime/rgb.txt b/runtime/rgb.txt deleted file mode 100644 index 5bc2baa3d6..0000000000 --- a/runtime/rgb.txt +++ /dev/null @@ -1,753 +0,0 @@ -! $XConsortium: rgb.txt,v 10.41 94/02/20 18:39:36 rws Exp $ -255 250 250 snow -248 248 255 ghost white -248 248 255 GhostWhite -245 245 245 white smoke -245 245 245 WhiteSmoke -220 220 220 gainsboro -255 250 240 floral white -255 250 240 FloralWhite -253 245 230 old lace -253 245 230 OldLace -250 240 230 linen -250 235 215 antique white -250 235 215 AntiqueWhite -255 239 213 papaya whip -255 239 213 PapayaWhip -255 235 205 blanched almond -255 235 205 BlanchedAlmond -255 228 196 bisque -255 218 185 peach puff -255 218 185 PeachPuff -255 222 173 navajo white -255 222 173 NavajoWhite -255 228 181 moccasin -255 248 220 cornsilk -255 255 240 ivory -255 250 205 lemon chiffon -255 250 205 LemonChiffon -255 245 238 seashell -240 255 240 honeydew -245 255 250 mint cream -245 255 250 MintCream -240 255 255 azure -240 248 255 alice blue -240 248 255 AliceBlue -230 230 250 lavender -255 240 245 lavender blush -255 240 245 LavenderBlush -255 228 225 misty rose -255 228 225 MistyRose -255 255 255 white - 0 0 0 black - 47 79 79 dark slate gray - 47 79 79 DarkSlateGray - 47 79 79 dark slate grey - 47 79 79 DarkSlateGrey -105 105 105 dim gray -105 105 105 DimGray -105 105 105 dim grey -105 105 105 DimGrey -112 128 144 slate gray -112 128 144 SlateGray -112 128 144 slate grey -112 128 144 SlateGrey -119 136 153 light slate gray -119 136 153 LightSlateGray -119 136 153 light slate grey -119 136 153 LightSlateGrey -190 190 190 gray -190 190 190 grey -211 211 211 light grey -211 211 211 LightGrey -211 211 211 light gray -211 211 211 LightGray - 25 25 112 midnight blue - 25 25 112 MidnightBlue - 0 0 128 navy - 0 0 128 navy blue - 0 0 128 NavyBlue -100 149 237 cornflower blue -100 149 237 CornflowerBlue - 72 61 139 dark slate blue - 72 61 139 DarkSlateBlue -106 90 205 slate blue -106 90 205 SlateBlue -123 104 238 medium slate blue -123 104 238 MediumSlateBlue -132 112 255 light slate blue -132 112 255 LightSlateBlue - 0 0 205 medium blue - 0 0 205 MediumBlue - 65 105 225 royal blue - 65 105 225 RoyalBlue - 0 0 255 blue - 30 144 255 dodger blue - 30 144 255 DodgerBlue - 0 191 255 deep sky blue - 0 191 255 DeepSkyBlue -135 206 235 sky blue -135 206 235 SkyBlue -135 206 250 light sky blue -135 206 250 LightSkyBlue - 70 130 180 steel blue - 70 130 180 SteelBlue -176 196 222 light steel blue -176 196 222 LightSteelBlue -173 216 230 light blue -173 216 230 LightBlue -176 224 230 powder blue -176 224 230 PowderBlue -175 238 238 pale turquoise -175 238 238 PaleTurquoise - 0 206 209 dark turquoise - 0 206 209 DarkTurquoise - 72 209 204 medium turquoise - 72 209 204 MediumTurquoise - 64 224 208 turquoise - 0 255 255 cyan -224 255 255 light cyan -224 255 255 LightCyan - 95 158 160 cadet blue - 95 158 160 CadetBlue -102 205 170 medium aquamarine -102 205 170 MediumAquamarine -127 255 212 aquamarine - 0 100 0 dark green - 0 100 0 DarkGreen - 85 107 47 dark olive green - 85 107 47 DarkOliveGreen -143 188 143 dark sea green -143 188 143 DarkSeaGreen - 46 139 87 sea green - 46 139 87 SeaGreen - 60 179 113 medium sea green - 60 179 113 MediumSeaGreen - 32 178 170 light sea green - 32 178 170 LightSeaGreen -152 251 152 pale green -152 251 152 PaleGreen - 0 255 127 spring green - 0 255 127 SpringGreen -124 252 0 lawn green -124 252 0 LawnGreen - 0 255 0 green -127 255 0 chartreuse - 0 250 154 medium spring green - 0 250 154 MediumSpringGreen -173 255 47 green yellow -173 255 47 GreenYellow - 50 205 50 lime green - 50 205 50 LimeGreen -154 205 50 yellow green -154 205 50 YellowGreen - 34 139 34 forest green - 34 139 34 ForestGreen -107 142 35 olive drab -107 142 35 OliveDrab -189 183 107 dark khaki -189 183 107 DarkKhaki -240 230 140 khaki -238 232 170 pale goldenrod -238 232 170 PaleGoldenrod -250 250 210 light goldenrod yellow -250 250 210 LightGoldenrodYellow -255 255 224 light yellow -255 255 224 LightYellow -255 255 0 yellow -255 215 0 gold -238 221 130 light goldenrod -238 221 130 LightGoldenrod -218 165 32 goldenrod -184 134 11 dark goldenrod -184 134 11 DarkGoldenrod -188 143 143 rosy brown -188 143 143 RosyBrown -205 92 92 indian red -205 92 92 IndianRed -139 69 19 saddle brown -139 69 19 SaddleBrown -160 82 45 sienna -205 133 63 peru -222 184 135 burlywood -245 245 220 beige -245 222 179 wheat -244 164 96 sandy brown -244 164 96 SandyBrown -210 180 140 tan -210 105 30 chocolate -178 34 34 firebrick -165 42 42 brown -233 150 122 dark salmon -233 150 122 DarkSalmon -250 128 114 salmon -255 160 122 light salmon -255 160 122 LightSalmon -255 165 0 orange -255 140 0 dark orange -255 140 0 DarkOrange -255 127 80 coral -240 128 128 light coral -240 128 128 LightCoral -255 99 71 tomato -255 69 0 orange red -255 69 0 OrangeRed -255 0 0 red -255 105 180 hot pink -255 105 180 HotPink -255 20 147 deep pink -255 20 147 DeepPink -255 192 203 pink -255 182 193 light pink -255 182 193 LightPink -219 112 147 pale violet red -219 112 147 PaleVioletRed -176 48 96 maroon -199 21 133 medium violet red -199 21 133 MediumVioletRed -208 32 144 violet red -208 32 144 VioletRed -255 0 255 magenta -238 130 238 violet -221 160 221 plum -218 112 214 orchid -186 85 211 medium orchid -186 85 211 MediumOrchid -153 50 204 dark orchid -153 50 204 DarkOrchid -148 0 211 dark violet -148 0 211 DarkViolet -138 43 226 blue violet -138 43 226 BlueViolet -160 32 240 purple -147 112 219 medium purple -147 112 219 MediumPurple -216 191 216 thistle -255 250 250 snow1 -238 233 233 snow2 -205 201 201 snow3 -139 137 137 snow4 -255 245 238 seashell1 -238 229 222 seashell2 -205 197 191 seashell3 -139 134 130 seashell4 -255 239 219 AntiqueWhite1 -238 223 204 AntiqueWhite2 -205 192 176 AntiqueWhite3 -139 131 120 AntiqueWhite4 -255 228 196 bisque1 -238 213 183 bisque2 -205 183 158 bisque3 -139 125 107 bisque4 -255 218 185 PeachPuff1 -238 203 173 PeachPuff2 -205 175 149 PeachPuff3 -139 119 101 PeachPuff4 -255 222 173 NavajoWhite1 -238 207 161 NavajoWhite2 -205 179 139 NavajoWhite3 -139 121 94 NavajoWhite4 -255 250 205 LemonChiffon1 -238 233 191 LemonChiffon2 -205 201 165 LemonChiffon3 -139 137 112 LemonChiffon4 -255 248 220 cornsilk1 -238 232 205 cornsilk2 -205 200 177 cornsilk3 -139 136 120 cornsilk4 -255 255 240 ivory1 -238 238 224 ivory2 -205 205 193 ivory3 -139 139 131 ivory4 -240 255 240 honeydew1 -224 238 224 honeydew2 -193 205 193 honeydew3 -131 139 131 honeydew4 -255 240 245 LavenderBlush1 -238 224 229 LavenderBlush2 -205 193 197 LavenderBlush3 -139 131 134 LavenderBlush4 -255 228 225 MistyRose1 -238 213 210 MistyRose2 -205 183 181 MistyRose3 -139 125 123 MistyRose4 -240 255 255 azure1 -224 238 238 azure2 -193 205 205 azure3 -131 139 139 azure4 -131 111 255 SlateBlue1 -122 103 238 SlateBlue2 -105 89 205 SlateBlue3 - 71 60 139 SlateBlue4 - 72 118 255 RoyalBlue1 - 67 110 238 RoyalBlue2 - 58 95 205 RoyalBlue3 - 39 64 139 RoyalBlue4 - 0 0 255 blue1 - 0 0 238 blue2 - 0 0 205 blue3 - 0 0 139 blue4 - 30 144 255 DodgerBlue1 - 28 134 238 DodgerBlue2 - 24 116 205 DodgerBlue3 - 16 78 139 DodgerBlue4 - 99 184 255 SteelBlue1 - 92 172 238 SteelBlue2 - 79 148 205 SteelBlue3 - 54 100 139 SteelBlue4 - 0 191 255 DeepSkyBlue1 - 0 178 238 DeepSkyBlue2 - 0 154 205 DeepSkyBlue3 - 0 104 139 DeepSkyBlue4 -135 206 255 SkyBlue1 -126 192 238 SkyBlue2 -108 166 205 SkyBlue3 - 74 112 139 SkyBlue4 -176 226 255 LightSkyBlue1 -164 211 238 LightSkyBlue2 -141 182 205 LightSkyBlue3 - 96 123 139 LightSkyBlue4 -198 226 255 SlateGray1 -185 211 238 SlateGray2 -159 182 205 SlateGray3 -108 123 139 SlateGray4 -202 225 255 LightSteelBlue1 -188 210 238 LightSteelBlue2 -162 181 205 LightSteelBlue3 -110 123 139 LightSteelBlue4 -191 239 255 LightBlue1 -178 223 238 LightBlue2 -154 192 205 LightBlue3 -104 131 139 LightBlue4 -224 255 255 LightCyan1 -209 238 238 LightCyan2 -180 205 205 LightCyan3 -122 139 139 LightCyan4 -187 255 255 PaleTurquoise1 -174 238 238 PaleTurquoise2 -150 205 205 PaleTurquoise3 -102 139 139 PaleTurquoise4 -152 245 255 CadetBlue1 -142 229 238 CadetBlue2 -122 197 205 CadetBlue3 - 83 134 139 CadetBlue4 - 0 245 255 turquoise1 - 0 229 238 turquoise2 - 0 197 205 turquoise3 - 0 134 139 turquoise4 - 0 255 255 cyan1 - 0 238 238 cyan2 - 0 205 205 cyan3 - 0 139 139 cyan4 -151 255 255 DarkSlateGray1 -141 238 238 DarkSlateGray2 -121 205 205 DarkSlateGray3 - 82 139 139 DarkSlateGray4 -127 255 212 aquamarine1 -118 238 198 aquamarine2 -102 205 170 aquamarine3 - 69 139 116 aquamarine4 -193 255 193 DarkSeaGreen1 -180 238 180 DarkSeaGreen2 -155 205 155 DarkSeaGreen3 -105 139 105 DarkSeaGreen4 - 84 255 159 SeaGreen1 - 78 238 148 SeaGreen2 - 67 205 128 SeaGreen3 - 46 139 87 SeaGreen4 -154 255 154 PaleGreen1 -144 238 144 PaleGreen2 -124 205 124 PaleGreen3 - 84 139 84 PaleGreen4 - 0 255 127 SpringGreen1 - 0 238 118 SpringGreen2 - 0 205 102 SpringGreen3 - 0 139 69 SpringGreen4 - 0 255 0 green1 - 0 238 0 green2 - 0 205 0 green3 - 0 139 0 green4 -127 255 0 chartreuse1 -118 238 0 chartreuse2 -102 205 0 chartreuse3 - 69 139 0 chartreuse4 -192 255 62 OliveDrab1 -179 238 58 OliveDrab2 -154 205 50 OliveDrab3 -105 139 34 OliveDrab4 -202 255 112 DarkOliveGreen1 -188 238 104 DarkOliveGreen2 -162 205 90 DarkOliveGreen3 -110 139 61 DarkOliveGreen4 -255 246 143 khaki1 -238 230 133 khaki2 -205 198 115 khaki3 -139 134 78 khaki4 -255 236 139 LightGoldenrod1 -238 220 130 LightGoldenrod2 -205 190 112 LightGoldenrod3 -139 129 76 LightGoldenrod4 -255 255 224 LightYellow1 -238 238 209 LightYellow2 -205 205 180 LightYellow3 -139 139 122 LightYellow4 -255 255 0 yellow1 -238 238 0 yellow2 -205 205 0 yellow3 -139 139 0 yellow4 -255 215 0 gold1 -238 201 0 gold2 -205 173 0 gold3 -139 117 0 gold4 -255 193 37 goldenrod1 -238 180 34 goldenrod2 -205 155 29 goldenrod3 -139 105 20 goldenrod4 -255 185 15 DarkGoldenrod1 -238 173 14 DarkGoldenrod2 -205 149 12 DarkGoldenrod3 -139 101 8 DarkGoldenrod4 -255 193 193 RosyBrown1 -238 180 180 RosyBrown2 -205 155 155 RosyBrown3 -139 105 105 RosyBrown4 -255 106 106 IndianRed1 -238 99 99 IndianRed2 -205 85 85 IndianRed3 -139 58 58 IndianRed4 -255 130 71 sienna1 -238 121 66 sienna2 -205 104 57 sienna3 -139 71 38 sienna4 -255 211 155 burlywood1 -238 197 145 burlywood2 -205 170 125 burlywood3 -139 115 85 burlywood4 -255 231 186 wheat1 -238 216 174 wheat2 -205 186 150 wheat3 -139 126 102 wheat4 -255 165 79 tan1 -238 154 73 tan2 -205 133 63 tan3 -139 90 43 tan4 -255 127 36 chocolate1 -238 118 33 chocolate2 -205 102 29 chocolate3 -139 69 19 chocolate4 -255 48 48 firebrick1 -238 44 44 firebrick2 -205 38 38 firebrick3 -139 26 26 firebrick4 -255 64 64 brown1 -238 59 59 brown2 -205 51 51 brown3 -139 35 35 brown4 -255 140 105 salmon1 -238 130 98 salmon2 -205 112 84 salmon3 -139 76 57 salmon4 -255 160 122 LightSalmon1 -238 149 114 LightSalmon2 -205 129 98 LightSalmon3 -139 87 66 LightSalmon4 -255 165 0 orange1 -238 154 0 orange2 -205 133 0 orange3 -139 90 0 orange4 -255 127 0 DarkOrange1 -238 118 0 DarkOrange2 -205 102 0 DarkOrange3 -139 69 0 DarkOrange4 -255 114 86 coral1 -238 106 80 coral2 -205 91 69 coral3 -139 62 47 coral4 -255 99 71 tomato1 -238 92 66 tomato2 -205 79 57 tomato3 -139 54 38 tomato4 -255 69 0 OrangeRed1 -238 64 0 OrangeRed2 -205 55 0 OrangeRed3 -139 37 0 OrangeRed4 -255 0 0 red1 -238 0 0 red2 -205 0 0 red3 -139 0 0 red4 -255 20 147 DeepPink1 -238 18 137 DeepPink2 -205 16 118 DeepPink3 -139 10 80 DeepPink4 -255 110 180 HotPink1 -238 106 167 HotPink2 -205 96 144 HotPink3 -139 58 98 HotPink4 -255 181 197 pink1 -238 169 184 pink2 -205 145 158 pink3 -139 99 108 pink4 -255 174 185 LightPink1 -238 162 173 LightPink2 -205 140 149 LightPink3 -139 95 101 LightPink4 -255 130 171 PaleVioletRed1 -238 121 159 PaleVioletRed2 -205 104 137 PaleVioletRed3 -139 71 93 PaleVioletRed4 -255 52 179 maroon1 -238 48 167 maroon2 -205 41 144 maroon3 -139 28 98 maroon4 -255 62 150 VioletRed1 -238 58 140 VioletRed2 -205 50 120 VioletRed3 -139 34 82 VioletRed4 -255 0 255 magenta1 -238 0 238 magenta2 -205 0 205 magenta3 -139 0 139 magenta4 -255 131 250 orchid1 -238 122 233 orchid2 -205 105 201 orchid3 -139 71 137 orchid4 -255 187 255 plum1 -238 174 238 plum2 -205 150 205 plum3 -139 102 139 plum4 -224 102 255 MediumOrchid1 -209 95 238 MediumOrchid2 -180 82 205 MediumOrchid3 -122 55 139 MediumOrchid4 -191 62 255 DarkOrchid1 -178 58 238 DarkOrchid2 -154 50 205 DarkOrchid3 -104 34 139 DarkOrchid4 -155 48 255 purple1 -145 44 238 purple2 -125 38 205 purple3 - 85 26 139 purple4 -171 130 255 MediumPurple1 -159 121 238 MediumPurple2 -137 104 205 MediumPurple3 - 93 71 139 MediumPurple4 -255 225 255 thistle1 -238 210 238 thistle2 -205 181 205 thistle3 -139 123 139 thistle4 - 0 0 0 gray0 - 0 0 0 grey0 - 3 3 3 gray1 - 3 3 3 grey1 - 5 5 5 gray2 - 5 5 5 grey2 - 8 8 8 gray3 - 8 8 8 grey3 - 10 10 10 gray4 - 10 10 10 grey4 - 13 13 13 gray5 - 13 13 13 grey5 - 15 15 15 gray6 - 15 15 15 grey6 - 18 18 18 gray7 - 18 18 18 grey7 - 20 20 20 gray8 - 20 20 20 grey8 - 23 23 23 gray9 - 23 23 23 grey9 - 26 26 26 gray10 - 26 26 26 grey10 - 28 28 28 gray11 - 28 28 28 grey11 - 31 31 31 gray12 - 31 31 31 grey12 - 33 33 33 gray13 - 33 33 33 grey13 - 36 36 36 gray14 - 36 36 36 grey14 - 38 38 38 gray15 - 38 38 38 grey15 - 41 41 41 gray16 - 41 41 41 grey16 - 43 43 43 gray17 - 43 43 43 grey17 - 46 46 46 gray18 - 46 46 46 grey18 - 48 48 48 gray19 - 48 48 48 grey19 - 51 51 51 gray20 - 51 51 51 grey20 - 54 54 54 gray21 - 54 54 54 grey21 - 56 56 56 gray22 - 56 56 56 grey22 - 59 59 59 gray23 - 59 59 59 grey23 - 61 61 61 gray24 - 61 61 61 grey24 - 64 64 64 gray25 - 64 64 64 grey25 - 66 66 66 gray26 - 66 66 66 grey26 - 69 69 69 gray27 - 69 69 69 grey27 - 71 71 71 gray28 - 71 71 71 grey28 - 74 74 74 gray29 - 74 74 74 grey29 - 77 77 77 gray30 - 77 77 77 grey30 - 79 79 79 gray31 - 79 79 79 grey31 - 82 82 82 gray32 - 82 82 82 grey32 - 84 84 84 gray33 - 84 84 84 grey33 - 87 87 87 gray34 - 87 87 87 grey34 - 89 89 89 gray35 - 89 89 89 grey35 - 92 92 92 gray36 - 92 92 92 grey36 - 94 94 94 gray37 - 94 94 94 grey37 - 97 97 97 gray38 - 97 97 97 grey38 - 99 99 99 gray39 - 99 99 99 grey39 -102 102 102 gray40 -102 102 102 grey40 -105 105 105 gray41 -105 105 105 grey41 -107 107 107 gray42 -107 107 107 grey42 -110 110 110 gray43 -110 110 110 grey43 -112 112 112 gray44 -112 112 112 grey44 -115 115 115 gray45 -115 115 115 grey45 -117 117 117 gray46 -117 117 117 grey46 -120 120 120 gray47 -120 120 120 grey47 -122 122 122 gray48 -122 122 122 grey48 -125 125 125 gray49 -125 125 125 grey49 -127 127 127 gray50 -127 127 127 grey50 -130 130 130 gray51 -130 130 130 grey51 -133 133 133 gray52 -133 133 133 grey52 -135 135 135 gray53 -135 135 135 grey53 -138 138 138 gray54 -138 138 138 grey54 -140 140 140 gray55 -140 140 140 grey55 -143 143 143 gray56 -143 143 143 grey56 -145 145 145 gray57 -145 145 145 grey57 -148 148 148 gray58 -148 148 148 grey58 -150 150 150 gray59 -150 150 150 grey59 -153 153 153 gray60 -153 153 153 grey60 -156 156 156 gray61 -156 156 156 grey61 -158 158 158 gray62 -158 158 158 grey62 -161 161 161 gray63 -161 161 161 grey63 -163 163 163 gray64 -163 163 163 grey64 -166 166 166 gray65 -166 166 166 grey65 -168 168 168 gray66 -168 168 168 grey66 -171 171 171 gray67 -171 171 171 grey67 -173 173 173 gray68 -173 173 173 grey68 -176 176 176 gray69 -176 176 176 grey69 -179 179 179 gray70 -179 179 179 grey70 -181 181 181 gray71 -181 181 181 grey71 -184 184 184 gray72 -184 184 184 grey72 -186 186 186 gray73 -186 186 186 grey73 -189 189 189 gray74 -189 189 189 grey74 -191 191 191 gray75 -191 191 191 grey75 -194 194 194 gray76 -194 194 194 grey76 -196 196 196 gray77 -196 196 196 grey77 -199 199 199 gray78 -199 199 199 grey78 -201 201 201 gray79 -201 201 201 grey79 -204 204 204 gray80 -204 204 204 grey80 -207 207 207 gray81 -207 207 207 grey81 -209 209 209 gray82 -209 209 209 grey82 -212 212 212 gray83 -212 212 212 grey83 -214 214 214 gray84 -214 214 214 grey84 -217 217 217 gray85 -217 217 217 grey85 -219 219 219 gray86 -219 219 219 grey86 -222 222 222 gray87 -222 222 222 grey87 -224 224 224 gray88 -224 224 224 grey88 -227 227 227 gray89 -227 227 227 grey89 -229 229 229 gray90 -229 229 229 grey90 -232 232 232 gray91 -232 232 232 grey91 -235 235 235 gray92 -235 235 235 grey92 -237 237 237 gray93 -237 237 237 grey93 -240 240 240 gray94 -240 240 240 grey94 -242 242 242 gray95 -242 242 242 grey95 -245 245 245 gray96 -245 245 245 grey96 -247 247 247 gray97 -247 247 247 grey97 -250 250 250 gray98 -250 250 250 grey98 -252 252 252 gray99 -252 252 252 grey99 -255 255 255 gray100 -255 255 255 grey100 -169 169 169 dark grey -169 169 169 DarkGrey -169 169 169 dark gray -169 169 169 DarkGray -0 0 139 dark blue -0 0 139 DarkBlue -0 139 139 dark cyan -0 139 139 DarkCyan -139 0 139 dark magenta -139 0 139 DarkMagenta -139 0 0 dark red -139 0 0 DarkRed -144 238 144 light green -144 238 144 LightGreen diff --git a/runtime/scripts.vim b/runtime/scripts.vim index 0b3fdc3514..3790b1c10f 100644 --- a/runtime/scripts.vim +++ b/runtime/scripts.vim @@ -190,6 +190,18 @@ if s:line1 =~# "^#!" elseif s:name =~# 'fennel\>' set ft=fennel + " MikroTik RouterOS script + elseif s:name =~# 'rsc\>' + set ft=routeros + + " Fish shell + elseif s:name =~# 'fish\>' + set ft=fish + + " Gforth + elseif s:name =~# 'gforth\>' + set ft=forth + endif unlet s:name @@ -390,6 +402,10 @@ else elseif s:line1 =~# '^%YAML' set ft=yaml + " MikroTik RouterOS script + elseif s:line1 =~# '^#.*by RouterOS.*$' + set ft=routeros + " CVS diff else let s:lnum = 1 diff --git a/runtime/syntax/autoit.vim b/runtime/syntax/autoit.vim index 1b9ab7458e..6b6048aadc 100644 --- a/runtime/syntax/autoit.vim +++ b/runtime/syntax/autoit.vim @@ -5,6 +5,7 @@ " Authored By: Riccardo Casini <ric@libero.it> " Script URL: http://www.vim.org/scripts/script.php?script_id=1239 " ChangeLog: Please visit the script URL for detailed change information +" Included change from #970. " Quit when a syntax file was already loaded. if exists("b:current_syntax") @@ -932,7 +933,7 @@ syn match autoitConst "\$SD_POWERDOWN" " constants - string syn match autoitConst "\$STR_NOCASESENSE" syn match autoitConst "\$STR_CASESENSE" -syn match autoitConst "\STR_STRIPLEADING" +syn match autoitConst "\$STR_STRIPLEADING" syn match autoitConst "\$STR_STRIPTRAILING" syn match autoitConst "\$STR_STRIPSPACES" syn match autoitConst "\$STR_STRIPALL" diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim index 20f8632006..e86e1b8669 100644 --- a/runtime/syntax/c.vim +++ b/runtime/syntax/c.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: C " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 May 24 +" Last Change: 2021 Dec 07 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") @@ -196,7 +196,6 @@ syn match cNumber display contained "0x\x\+\(u\=l\{0,2}\|ll\=u\)\>" " Flag the first zero of an octal number as something special syn match cOctal display contained "0\o\+\(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero syn match cOctalZero display contained "\<0" -syn match cFloat display contained "\d\+f" "floating point number, with dot, optional exponent syn match cFloat display contained "\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\=" "floating point number, starting with a dot, optional exponent diff --git a/runtime/syntax/checkhealth.vim b/runtime/syntax/checkhealth.vim new file mode 100644 index 0000000000..dff880a0bc --- /dev/null +++ b/runtime/syntax/checkhealth.vim @@ -0,0 +1,28 @@ +" Vim syntax file +" Language: Neovim checkhealth buffer +" Last Change: 2021 Dec 15 + +if exists("b:current_syntax") + finish +endif + +runtime! syntax/markdown.vim +unlet! b:current_syntax + +syn case match + +" We do not care about markdown syntax errors +syn clear markdownError + +syn keyword healthError ERROR[:] containedin=markdownCodeBlock,mkdListItemLine +syn keyword healthWarning WARNING[:] containedin=markdownCodeBlock,mkdListItemLine +syn keyword healthSuccess OK[:] containedin=markdownCodeBlock,mkdListItemLine +syn match healthHelp "|.\{-}|" containedin=markdownCodeBlock,mkdListItemLine contains=healthBar +syn match healthBar "|" contained conceal + +hi def link healthError Error +hi def link healthWarning WarningMsg +hi def healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232 +hi def link healthHelp Identifier + +let b:current_syntax = "checkhealth" diff --git a/runtime/syntax/css.vim b/runtime/syntax/css.vim index 67ad1ea335..564dc151bc 100644 --- a/runtime/syntax/css.vim +++ b/runtime/syntax/css.vim @@ -7,7 +7,7 @@ " Nikolai Weibull (Add CSS2 support) " URL: https://github.com/vim-language-dept/css-syntax.vim " Maintainer: Jay Sitter <jay@jaysitter.com> -" Last Change: 2021 Oct 15 +" Last Change: 2021 Oct 20 " quit when a syntax file was already loaded if !exists("main_syntax") @@ -116,7 +116,7 @@ syn keyword cssColor contained ActiveBorder ActiveCaption AppWorkspace ButtonFac syn case ignore syn match cssImportant contained "!\s*important\>" -syn match cssCustomProp contained "--[a-zA-Z0-9-_]*" +syn match cssCustomProp contained "\<--[a-zA-Z0-9-_]*\>" syn match cssColor contained "\<transparent\>" syn match cssColor contained "\<currentColor\>" @@ -126,6 +126,7 @@ syn match cssColor contained "#\x\{6\}\>" contains=cssUnitDecorators syn match cssColor contained "#\x\{8\}\>" contains=cssUnitDecorators syn region cssURL contained matchgroup=cssFunctionName start="\<\(uri\|url\|local\|format\)\s*(" end=")" contains=cssStringQ,cssStringQQ oneline +syn region cssMathGroup contained matchgroup=cssMathParens start="(" end=")" containedin=cssFunction,cssMathGroup contains=cssCustomProp,cssValue.*,cssFunction,cssColor,cssStringQ,cssStringQQ oneline syn region cssFunction contained matchgroup=cssFunctionName start="\<\(var\|calc\)\s*(" end=")" contains=cssCustomProp,cssValue.*,cssFunction,cssColor,cssStringQ,cssStringQQ oneline syn region cssFunction contained matchgroup=cssFunctionName start="\<\(rgb\|clip\|attr\|counter\|rect\|cubic-bezier\|steps\)\s*(" end=")" oneline contains=cssValueInteger,cssValueNumber,cssValueLength,cssFunctionComma syn region cssFunction contained matchgroup=cssFunctionName start="\<\(rgba\|hsl\|hsla\|color-stop\|from\|to\)\s*(" end=")" oneline contains=cssColor,cssValueInteger,cssValueNumber,cssValueLength,cssFunctionComma,cssFunction @@ -395,9 +396,9 @@ syn match cssUIAttr contained '\<preserve-3d\>' syn match cssIEUIAttr contained '\<bicubic\>' " Webkit/iOS specific properties -syn match cssUIProp contained '\<tap-highlight-color\|user-select\|touch-callout\>' +syn match cssUIProp contained '\<\(tap-highlight-color\|user-select\|touch-callout\)\>' " IE specific properties -syn match cssIEUIProp contained '\<interpolation-mode\|zoom\|filter\>' +syn match cssIEUIProp contained '\<\(interpolation-mode\|zoom\|filter\)\>' " Webkit/Firebox specific properties/attributes syn keyword cssUIProp contained appearance @@ -423,11 +424,15 @@ syn keyword cssAuralAttr contained male female child code digits continuous syn match cssMobileTextProp contained "\<text-size-adjust\>" syn keyword cssMediaProp contained width height orientation scan -syn match cssMediaProp contained /\(\(max\|min\)-\)\=\(\(device\)-\)\=aspect-ratio/ -syn match cssMediaProp contained /\(\(max\|min\)-\)\=device-pixel-ratio/ -syn match cssMediaProp contained /\(\(max\|min\)-\)\=device-\(height\|width\)/ -syn match cssMediaProp contained /\(\(max\|min\)-\)\=\(height\|width\|resolution\|monochrome\|color\(-index\)\=\)/ +syn keyword cssMediaProp contained any-hover any-pointer color-gamut grid hover +syn keyword cssMediaProp contained overflow-block overflow-inline pointer update +syn match cssMediaProp contained /\<\(\(max\|min\)-\)\=\(\(device\)-\)\=aspect-ratio\>/ +syn match cssMediaProp contained /\<\(\(max\|min\)-\)\=device-pixel-ratio\>/ +syn match cssMediaProp contained /\<\(\(max\|min\)-\)\=device-\(height\|width\)\>/ +syn match cssMediaProp contained /\<\(\(max\|min\)-\)\=\(height\|width\|resolution\|monochrome\|color\(-index\)\=\)\>/ syn keyword cssMediaAttr contained portrait landscape progressive interlace +syn keyword cssMediaAttr contained coarse fast fine hover infinite p3 paged +syn keyword cssMediaAttr contained rec2020 scroll slow srgb syn match cssKeyFrameProp contained /\(\d\+\(\.\d\+\)\?%\|\(\<from\|to\>\)\)/ nextgroup=cssDefinition syn match cssPageMarginProp /@\(\(top\|left\|right\|bottom\)-\(left\|center\|right\|middle\|bottom\)\)\(-corner\)\=/ contained nextgroup=cssDefinition syn keyword cssPageProp contained content size @@ -445,17 +450,17 @@ syn match cssBraceError "}" syn match cssAttrComma "," " Pseudo class -" http://www.w3.org/TR/css3-selectors/ +" https://www.w3.org/TR/selectors-4/ syn match cssPseudoClass ":[A-Za-z0-9_-]*" contains=cssNoise,cssPseudoClassId,cssUnicodeEscape,cssVendor,cssPseudoClassFn syn keyword cssPseudoClassId contained link visited active hover before after left right -syn keyword cssPseudoClassId contained root empty target enable disabled checked invalid +syn keyword cssPseudoClassId contained root empty target enabled disabled checked invalid syn match cssPseudoClassId contained "\<first-\(line\|letter\)\>" syn match cssPseudoClassId contained "\<\(first\|last\|only\)-\(of-type\|child\)\>" -syn region cssPseudoClassFn contained matchgroup=cssFunctionName start="\<\(not\|lang\|\(nth\|nth-last\)-\(of-type\|child\)\)(" end=")" contains=cssStringQ,cssStringQQ +syn match cssPseudoClassId contained "\<focus\(-within\|-visible\)\=\>" +syn region cssPseudoClassFn contained matchgroup=cssFunctionName start="\<\(not\|is\|lang\|\(nth\|nth-last\)-\(of-type\|child\)\)(" end=")" contains=cssStringQ,cssStringQQ,cssTagName,cssAttributeSelector,cssClassName,cssIdentifier " ------------------------------------ " Vendor specific properties syn match cssPseudoClassId contained "\<selection\>" -syn match cssPseudoClassId contained "\<focus\(-inner\)\=\>" syn match cssPseudoClassId contained "\<\(input-\)\=placeholder\>" " Misc highlight groups diff --git a/runtime/syntax/django.vim b/runtime/syntax/django.vim index d3ca4de0e2..76b47d2e59 100644 --- a/runtime/syntax/django.vim +++ b/runtime/syntax/django.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Django template " Maintainer: Dave Hodder <dmh@dmh.org.uk> -" Last Change: 2014 Jul 13 +" Last Change: 2021 Nov 29 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -31,6 +31,7 @@ syn keyword djangoStatement contained closecomment widthratio url with endwith syn keyword djangoStatement contained get_current_language trans noop blocktrans syn keyword djangoStatement contained endblocktrans get_available_languages syn keyword djangoStatement contained get_current_language_bidi plural +syn keyword djangoStatement contained translate blocktranslate endblocktranslate " Django templete built-in filters syn keyword djangoFilter contained add addslashes capfirst center cut date diff --git a/runtime/syntax/gdb.vim b/runtime/syntax/gdb.vim index 25a21d184e..c820ba40a9 100644 --- a/runtime/syntax/gdb.vim +++ b/runtime/syntax/gdb.vim @@ -2,7 +2,8 @@ " Language: GDB command files " Maintainer: Claudio Fleiner <claudio@fleiner.com> " URL: http://www.fleiner.com/vim/syntax/gdb.vim -" Last Change: 2012 Oct 05 +" Last Change: 2021 Nov 15 +" Additional changes by Simon Sobisch " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -21,19 +22,19 @@ syn match gdbInfo contained "all-registers" syn keyword gdbStatement contained actions apply attach awatch backtrace break bt call catch cd clear collect commands -syn keyword gdbStatement contained complete condition continue delete detach directory disable disassemble display down +syn keyword gdbStatement contained complete condition continue delete detach directory disable disas[semble] disp[lay] down syn keyword gdbStatement contained echo else enable end file finish frame handle hbreak help if ignore syn keyword gdbStatement contained inspect jump kill list load maintenance make next nexti ni output overlay -syn keyword gdbStatement contained passcount path print printf ptype pwd quit rbreak remote return run rwatch -syn keyword gdbStatement contained search section set sharedlibrary shell show si signal source step stepi stepping +syn keyword gdbStatement contained passcount path print printf ptype python pwd quit rbreak remote return run rwatch +syn keyword gdbStatement contained search section set sharedlibrary shell show si signal skip source step stepi stepping syn keyword gdbStatement contained stop target tbreak tdump tfind thbreak thread tp trace tstart tstatus tstop -syn keyword gdbStatement contained tty undisplay unset until up watch whatis where while ws x +syn keyword gdbStatement contained tty und[isplay] unset until up watch whatis where while ws x syn match gdbFuncDef "\<define\>.*" syn match gdbStatmentContainer "^\s*\S\+" contains=gdbStatement,gdbFuncDef syn match gdbStatement "^\s*info" nextgroup=gdbInfo skipwhite skipempty " some commonly used abbreviations -syn keyword gdbStatement c disp undisp disas p +syn keyword gdbStatement c cont p py syn region gdbDocument matchgroup=gdbFuncDef start="\<document\>.*$" matchgroup=gdbFuncDef end="^end\s*$" diff --git a/runtime/syntax/gnuplot.vim b/runtime/syntax/gnuplot.vim index 9c98e67e58..b5092d7555 100644 --- a/runtime/syntax/gnuplot.vim +++ b/runtime/syntax/gnuplot.vim @@ -3,7 +3,8 @@ " Maintainer: Josh Wainwright <wainwright DOT ja AT gmail DOT com> " Last Maintainer: Andrew Rasmussen andyras@users.sourceforge.net " Original Maintainer: John Hoelzel johnh51@users.sourceforge.net -" Last Change: 2020 May 12 +" Last Change: 2021 Nov 16 +" additional changes from PR #8949 " Filenames: *.gnu *.plt *.gpi *.gih *.gp *.gnuplot scripts: #!*gnuplot " URL: http://www.vim.org/scripts/script.php?script_id=4873 " Original URL: http://johnh51.get.to/vim/syntax/gnuplot.vim @@ -32,22 +33,22 @@ syn match gnuplotSpecial "\\." contained " syn match gnuplotSpecial "\\\o\o\o\|\\x\x\x\|\\c[^"]\|\\[a-z\\]" contained " measurements in the units in, cm and pt are special -syn match gnuplotUnit "[0-9]+in" -syn match gnuplotUnit "[0-9]+cm" -syn match gnuplotUnit "[0-9]+pt" +syn match gnuplotUnit "\d+in" +syn match gnuplotUnit "\d+cm" +syn match gnuplotUnit "\d+pt" " external (shell) commands are special -syn region gnuplotExternal start="!" end="$" +syn region gnuplotExternal start="^\s*!" end="$" " ---- Comments ---- " -syn region gnuplotComment start="#" end="$" contains=gnuplotTodo +syn region gnuplotComment start="#" end="$" contains=gnuplotTodo,@Spell " ---- Constants ---- " " strings -syn region gnuplotString start=+"+ skip=+\\"+ end=+"+ contains=gnuplotSpecial -syn region gnuplotString start="'" end="'" +syn region gnuplotString start=+"+ skip=+\\"+ end=+"+ contains=gnuplotSpecial,@Spell +syn region gnuplotString start="'" end="'" contains=@Spell " built-in variables syn keyword gnuplotNumber GNUTERM GPVAL_TERM GPVAL_TERMOPTIONS GPVAL_SPLOT @@ -76,7 +77,7 @@ syn keyword gnuplotNumber GPVAL_TERM_YSIZE GPVAL_VIEW_MAP GPVAL_VIEW_ROT_X syn keyword gnuplotNumber GPVAL_VIEW_ROT_Z GPVAL_VIEW_SCALE " function name variables -syn match gnuplotNumber "GPFUN_[a-zA-Z_]*" +syn match gnuplotNumber "GPFUN_\h*" " stats variables syn keyword gnuplotNumber STATS_records STATS_outofrange STATS_invalid @@ -104,23 +105,23 @@ syn keyword gnuplotError FIT_LAMBDA_FACTOR FIT_LOG FIT_SCRIPT " integer number, or floating point number without a dot and with "f". syn case ignore -syn match gnuplotNumber "\<[0-9]\+\(u\=l\=\|lu\|f\)\>" +syn match gnuplotNumber "\<\d\+\(u\=l\=\|lu\|f\)\>" " floating point number, with dot, optional exponent -syn match gnuplotFloat "\<[0-9]\+\.[0-9]*\(e[-+]\=[0-9]\+\)\=[fl]\=\>" +syn match gnuplotFloat "\<\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\=\>" " floating point number, starting with a dot, optional exponent -syn match gnuplotFloat "\.[0-9]\+\(e[-+]\=[0-9]\+\)\=[fl]\=\>" +syn match gnuplotFloat "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>" " floating point number, without dot, with exponent -syn match gnuplotFloat "\<[0-9]\+e[-+]\=[0-9]\+[fl]\=\>" +syn match gnuplotFloat "\<\d\+e[-+]\=\d\+[fl]\=\>" " hex number -syn match gnuplotNumber "\<0x[0-9a-f]\+\(u\=l\=\|lu\)\>" +syn match gnuplotNumber "\<0x\x\+\(u\=l\=\|lu\)\>" syn case match " flag an octal number with wrong digits by not highlighting -syn match gnuplotOctalError "\<0[0-7]*[89]" +syn match gnuplotOctalError "\<0\o*[89]" " ---- Identifiers: Functions ---- " @@ -374,8 +375,8 @@ syn keyword gnuplotKeyword nohead nooutliers nowedge off opaque outliers syn keyword gnuplotKeyword palette pattern pi pointinterval pointsize syn keyword gnuplotKeyword pointtype ps pt radius range rectangle syn keyword gnuplotKeyword rowstacked screen separation size solid sorted -syn keyword gnuplotKeyword textbox transparent units unsorted userstyles -syn keyword gnuplotKeyword wedge x x2 xx xy yy +syn keyword gnuplotKeyword textbox units unsorted userstyles wedge +syn keyword gnuplotKeyword x x2 xx xy yy " set surface syn keyword gnuplotKeyword surface implicit explicit " set table @@ -477,9 +478,13 @@ syn keyword gnuplotKeyword nooutput " keywords for 'test' command syn keyword gnuplotKeyword terminal palette rgb rbg grb gbr brg bgr +" The transparent gnuplot keyword cannot use 'syn keyword' as transparent +" has a special meaning in :syntax commands. +syn match gnuplotKeyword "\<transparent\>" + " ---- Macros ---- " -syn match gnuplotMacro "@[a-zA-Z0-9_]*" +syn match gnuplotMacro "@\w*" " ---- Todos ---- " diff --git a/runtime/syntax/i3config.vim b/runtime/syntax/i3config.vim new file mode 100644 index 0000000000..a8b6637140 --- /dev/null +++ b/runtime/syntax/i3config.vim @@ -0,0 +1,256 @@ +" Vim syntax file +" Language: i3 config file +" Maintainer: Mohamed Boughaba <mohamed dot bgb at gmail dot com> +" Version: 0.4 +" Last Change: 2021 Dec 14 + +" References: +" http://i3wm.org/docs/userguide.html#configuring +" http://vimdoc.sourceforge.net/htmldoc/syntax.html +" +" +" Quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +scriptencoding utf-8 + +" Error +syn match i3ConfigError /.*/ + +" Todo +syn keyword i3ConfigTodo TODO FIXME XXX contained + +" Comment +" Comments are started with a # and can only be used at the beginning of a line +syn match i3ConfigComment /^\s*#.*$/ contains=i3ConfigTodo + +" Font +" A FreeType font description is composed by: +" a font family, a style, a weight, a variant, a stretch and a size. +syn match i3ConfigFontSeparator /,/ contained +syn match i3ConfigFontSeparator /:/ contained +syn keyword i3ConfigFontKeyword font contained +syn match i3ConfigFontNamespace /\w\+:/ contained contains=i3ConfigFontSeparator +syn match i3ConfigFontContent /-\?\w\+\(-\+\|\s\+\|,\)/ contained contains=i3ConfigFontNamespace,i3ConfigFontSeparator,i3ConfigFontKeyword +syn match i3ConfigFontSize /\s\=\d\+\(px\)\?\s\?$/ contained +syn match i3ConfigFont /^\s*font\s\+.*$/ contains=i3ConfigFontContent,i3ConfigFontSeparator,i3ConfigFontSize,i3ConfigFontNamespace +syn match i3ConfigFont /^\s*font\s\+.*\(\\\_.*\)\?$/ contains=i3ConfigFontContent,i3ConfigFontSeparator,i3ConfigFontSize,i3ConfigFontNamespace +syn match i3ConfigFont /^\s*font\s\+.*\(\\\_.*\)\?[^\\]\+$/ contains=i3ConfigFontContent,i3ConfigFontSeparator,i3ConfigFontSize,i3ConfigFontNamespace +syn match i3ConfigFont /^\s*font\s\+\(\(.*\\\_.*\)\|\(.*[^\\]\+$\)\)/ contains=i3ConfigFontContent,i3ConfigFontSeparator,i3ConfigFontSize,i3ConfigFontNamespace + +" variables +syn match i3ConfigString /\(['"]\)\(.\{-}\)\1/ contained +syn match i3ConfigColor /#\w\{6}/ contained +syn match i3ConfigVariableModifier /+/ contained +syn match i3ConfigVariableAndModifier /+\w\+/ contained contains=i3ConfigVariableModifier +syn match i3ConfigVariable /\$\w\+\(\(-\w\+\)\+\)\?\(\s\|+\)\?/ contains=i3ConfigVariableModifier,i3ConfigVariableAndModifier +syn keyword i3ConfigInitializeKeyword set contained +syn match i3ConfigInitialize /^\s*set\s\+.*$/ contains=i3ConfigVariable,i3ConfigInitializeKeyword,i3ConfigColor,i3ConfigString + +" Gaps +syn keyword i3ConfigGapStyleKeyword inner outer horizontal vertical top right bottom left current all set plus minus toggle up down contained +syn match i3ConfigGapStyle /^\s*\(gaps\)\s\+\(inner\|outer\|horizontal\|vertical\|left\|top\|right\|bottom\)\(\s\+\(current\|all\)\)\?\(\s\+\(set\|plus\|minus\|toggle\)\)\?\(\s\+\(-\?\d\+\|\$.*\)\)$/ contains=i3ConfigGapStyleKeyword,i3ConfigNumber,i3ConfigVariable +syn keyword i3ConfigSmartGapKeyword on inverse_outer contained +syn match i3ConfigSmartGap /^\s*smart_gaps\s\+\(on\|inverse_outer\)\s\?$/ contains=i3ConfigSmartGapKeyword +syn keyword i3ConfigSmartBorderKeyword on no_gaps contained +syn match i3ConfigSmartBorder /^\s*smart_borders\s\+\(on\|no_gaps\)\s\?$/ contains=i3ConfigSmartBorderKeyword + +" Keyboard bindings +syn keyword i3ConfigAction toggle fullscreen restart key import kill shrink grow contained +syn keyword i3ConfigAction focus move grow height width split layout resize restore reload mute unmute exit mode workspace container to contained +syn match i3ConfigModifier /\w\++\w\+\(\(+\w\+\)\+\)\?/ contained contains=i3ConfigVariableModifier +syn match i3ConfigNumber /\s\d\+/ contained +syn match i3ConfigUnit /\sp\(pt\|x\)/ contained +syn match i3ConfigUnitOr /\sor/ contained +syn keyword i3ConfigBindKeyword bindsym bindcode exec gaps border contained +syn match i3ConfigBindArgument /--\w\+\(\(-\w\+\)\+\)\?\s/ contained +syn match i3ConfigBind /^\s*\(bindsym\|bindcode\)\s\+.*$/ contains=i3ConfigVariable,i3ConfigBindKeyword,i3ConfigVariableAndModifier,i3ConfigNumber,i3ConfigUnit,i3ConfigUnitOr,i3ConfigBindArgument,i3ConfigModifier,i3ConfigAction,i3ConfigString,i3ConfigGapStyleKeyword,i3ConfigBorderStyleKeyword + +" Floating +syn keyword i3ConfigSizeSpecial x contained +syn match i3ConfigNegativeSize /-/ contained +syn match i3ConfigSize /-\?\d\+\s\?x\s\?-\?\d\+/ contained contains=i3ConfigSizeSpecial,i3ConfigNumber,i3ConfigNegativeSize +syn match i3ConfigFloating /^\s*floating_modifier\s\+\$\w\+\d\?/ contains=i3ConfigVariable +syn match i3ConfigFloating /^\s*floating_\(maximum\|minimum\)_size\s\+-\?\d\+\s\?x\s\?-\?\d\+/ contains=i3ConfigSize + +" Orientation +syn keyword i3ConfigOrientationKeyword vertical horizontal auto contained +syn match i3ConfigOrientation /^\s*default_orientation\s\+\(vertical\|horizontal\|auto\)\s\?$/ contains=i3ConfigOrientationKeyword + +" Layout +syn keyword i3ConfigLayoutKeyword default stacking tabbed contained +syn match i3ConfigLayout /^\s*workspace_layout\s\+\(default\|stacking\|tabbed\)\s\?$/ contains=i3ConfigLayoutKeyword + +" Border style +syn keyword i3ConfigBorderStyleKeyword none normal pixel contained +syn match i3ConfigBorderStyle /^\s*\(new_window\|new_float\|default_border\|default_floating_border\)\s\+\(none\|\(normal\|pixel\)\(\s\+\d\+\)\?\(\s\+\$\w\+\(\(-\w\+\)\+\)\?\(\s\|+\)\?\)\?\)\s\?$/ contains=i3ConfigBorderStyleKeyword,i3ConfigNumber,i3ConfigVariable + +" Hide borders and edges +syn keyword i3ConfigEdgeKeyword none vertical horizontal both smart smart_no_gaps contained +syn match i3ConfigEdge /^\s*hide_edge_borders\s\+\(none\|vertical\|horizontal\|both\|smart\|smart_no_gaps\)\s\?$/ contains=i3ConfigEdgeKeyword + +" Arbitrary commands for specific windows (for_window) +syn keyword i3ConfigCommandKeyword for_window contained +syn region i3ConfigWindowStringSpecial start=+"+ skip=+\\"+ end=+"+ contained contains=i3ConfigString +syn region i3ConfigWindowCommandSpecial start="\[" end="\]" contained contains=i3ConfigWindowStringSpacial,i3ConfigString +syn match i3ConfigArbitraryCommand /^\s*for_window\s\+.*$/ contains=i3ConfigWindowCommandSpecial,i3ConfigCommandKeyword,i3ConfigBorderStyleKeyword,i3ConfigLayoutKeyword,i3ConfigOrientationKeyword,Size,i3ConfigNumber + +" Disable focus open opening +syn keyword i3ConfigNoFocusKeyword no_focus contained +syn match i3ConfigDisableFocus /^\s*no_focus\s\+.*$/ contains=i3ConfigWindowCommandSpecial,i3ConfigNoFocusKeyword + +" Move client to specific workspace automatically +syn keyword i3ConfigAssignKeyword assign contained +syn match i3ConfigAssignSpecial /→/ contained +syn match i3ConfigAssign /^\s*assign\s\+.*$/ contains=i3ConfigAssignKeyword,i3ConfigWindowCommandSpecial,i3ConfigAssignSpecial + +" X resources +syn keyword i3ConfigResourceKeyword set_from_resource contained +syn match i3ConfigResource /^\s*set_from_resource\s\+.*$/ contains=i3ConfigResourceKeyword,i3ConfigWindowCommandSpecial,i3ConfigColor,i3ConfigVariable + +" Auto start applications +syn keyword i3ConfigExecKeyword exec exec_always contained +syn match i3ConfigNoStartupId /--no-startup-id/ contained " We are not using i3ConfigBindArgument as only no-startup-id is supported here +syn match i3ConfigExec /^\s*exec\(_always\)\?\s\+.*$/ contains=i3ConfigExecKeyword,i3ConfigNoStartupId,i3ConfigString + +" Automatically putting workspaces on specific screens +syn keyword i3ConfigWorkspaceKeyword workspace contained +syn keyword i3ConfigOutput output contained +syn match i3ConfigWorkspace /^\s*workspace\s\+.*$/ contains=i3ConfigWorkspaceKeyword,i3ConfigNumber,i3ConfigString,i3ConfigOutput + +" Changing colors +syn keyword i3ConfigClientColorKeyword client focused focused_inactive unfocused urgent placeholder background contained +syn match i3ConfigClientColor /^\s*client.\w\+\s\+.*$/ contains=i3ConfigClientColorKeyword,i3ConfigColor,i3ConfigVariable + +syn keyword i3ConfigTitleAlignKeyword left center right contained +syn match i3ConfigTitleAlign /^\s*title_align\s\+.*$/ contains=i3ConfigTitleAlignKeyword + +" Interprocess communication +syn match i3ConfigInterprocessKeyword /ipc-socket/ contained +syn match i3ConfigInterprocess /^\s*ipc-socket\s\+.*$/ contains=i3ConfigInterprocessKeyword + +" Mouse warping +syn keyword i3ConfigMouseWarpingKeyword mouse_warping contained +syn keyword i3ConfigMouseWarpingType output none contained +syn match i3ConfigMouseWarping /^\s*mouse_warping\s\+\(output\|none\)\s\?$/ contains=i3ConfigMouseWarpingKeyword,i3ConfigMouseWarpingType + +" Focus follows mouse +syn keyword i3ConfigFocusFollowsMouseKeyword focus_follows_mouse contained +syn keyword i3ConfigFocusFollowsMouseType yes no contained +syn match i3ConfigFocusFollowsMouse /^\s*focus_follows_mouse\s\+\(yes\|no\)\s\?$/ contains=i3ConfigFocusFollowsMouseKeyword,i3ConfigFocusFollowsMouseType + +" Popups during fullscreen mode +syn keyword i3ConfigPopupOnFullscreenKeyword popup_during_fullscreen contained +syn keyword i3ConfigPopuponFullscreenType smart ignore leave_fullscreen contained +syn match i3ConfigPopupOnFullscreen /^\s*popup_during_fullscreen\s\+\w\+\s\?$/ contains=i3ConfigPopupOnFullscreenKeyword,i3ConfigPopupOnFullscreenType + +" Focus wrapping +syn keyword i3ConfigFocusWrappingKeyword force_focus_wrapping focus_wrapping contained +syn keyword i3ConfigFocusWrappingType yes no contained +syn match i3ConfigFocusWrapping /^\s*\(force_\)\?focus_wrapping\s\+\(yes\|no\)\s\?$/ contains=i3ConfigFocusWrappingType,i3ConfigFocusWrappingKeyword + +" Forcing Xinerama +syn keyword i3ConfigForceXineramaKeyword force_xinerama contained +syn match i3ConfigForceXinerama /^\s*force_xinerama\s\+\(yes\|no\)\s\?$/ contains=i3ConfigFocusWrappingType,i3ConfigForceXineramaKeyword + +" Automatic back-and-forth when switching to the current workspace +syn keyword i3ConfigAutomaticSwitchKeyword workspace_auto_back_and_forth contained +syn match i3ConfigAutomaticSwitch /^\s*workspace_auto_back_and_forth\s\+\(yes\|no\)\s\?$/ contains=i3ConfigFocusWrappingType,i3ConfigAutomaticSwitchKeyword + +" Delay urgency hint +syn keyword i3ConfigTimeUnit ms contained +syn keyword i3ConfigDelayUrgencyKeyword force_display_urgency_hint contained +syn match i3ConfigDelayUrgency /^\s*force_display_urgency_hint\s\+\d\+\s\+ms\s\?$/ contains=i3ConfigFocusWrappingType,i3ConfigDelayUrgencyKeyword,i3ConfigNumber,i3ConfigTimeUnit + +" Focus on window activation +syn keyword i3ConfigFocusOnActivationKeyword focus_on_window_activation contained +syn keyword i3ConfigFocusOnActivationType smart urgent focus none contained +syn match i3ConfigFocusOnActivation /^\s*focus_on_window_activation\s\+\(smart\|urgent\|focus\|none\)\s\?$/ contains=i3ConfigFocusOnActivationKeyword,i3ConfigFocusOnActivationType + +" Automatic back-and-forth when switching to the current workspace +syn keyword i3ConfigDrawingMarksKeyword show_marks contained +syn match i3ConfigDrawingMarks /^\s*show_marks\s\+\(yes\|no\)\s\?$/ contains=i3ConfigFocusWrappingType,i3ConfigDrawingMarksKeyword + +" Group mode/bar +syn keyword i3ConfigBlockKeyword mode bar colors i3bar_command status_command position exec mode hidden_state modifier id position output background statusline tray_output tray_padding separator separator_symbol workspace_buttons strip_workspace_numbers binding_mode_indicator focused_workspace active_workspace inactive_workspace urgent_workspace binding_mode contained +syn region i3ConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend + +" Line continuation +syn region i3ConfigLineCont start=/^.*\\$/ end=/^.*$/ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend + +" Define the highlighting. +hi def link i3ConfigError Error +hi def link i3ConfigTodo Todo +hi def link i3ConfigComment Comment +hi def link i3ConfigFontContent Type +hi def link i3ConfigFocusOnActivationType Type +hi def link i3ConfigPopupOnFullscreenType Type +hi def link i3ConfigOrientationKeyword Type +hi def link i3ConfigMouseWarpingType Type +hi def link i3ConfigFocusFollowsMouseType Type +hi def link i3ConfigGapStyleKeyword Type +hi def link i3ConfigTitleAlignKeyword Type +hi def link i3ConfigSmartGapKeyword Type +hi def link i3ConfigSmartBorderKeyword Type +hi def link i3ConfigLayoutKeyword Type +hi def link i3ConfigBorderStyleKeyword Type +hi def link i3ConfigEdgeKeyword Type +hi def link i3ConfigAction Type +hi def link i3ConfigCommand Type +hi def link i3ConfigOutput Type +hi def link i3ConfigWindowCommandSpecial Type +hi def link i3ConfigFocusWrappingType Type +hi def link i3ConfigUnitOr Type +hi def link i3ConfigFontSize Constant +hi def link i3ConfigColor Constant +hi def link i3ConfigNumber Constant +hi def link i3ConfigUnit Constant +hi def link i3ConfigVariableAndModifier Constant +hi def link i3ConfigTimeUnit Constant +hi def link i3ConfigModifier Constant +hi def link i3ConfigString Constant +hi def link i3ConfigNegativeSize Constant +hi def link i3ConfigFontSeparator Special +hi def link i3ConfigVariableModifier Special +hi def link i3ConfigSizeSpecial Special +hi def link i3ConfigWindowSpecial Special +hi def link i3ConfigAssignSpecial Special +hi def link i3ConfigFontNamespace PreProc +hi def link i3ConfigBindArgument PreProc +hi def link i3ConfigNoStartupId PreProc +hi def link i3ConfigFontKeyword Identifier +hi def link i3ConfigBindKeyword Identifier +hi def link i3ConfigOrientation Identifier +hi def link i3ConfigGapStyle Identifier +hi def link i3ConfigTitleAlign Identifier +hi def link i3ConfigSmartGap Identifier +hi def link i3ConfigSmartBorder Identifier +hi def link i3ConfigLayout Identifier +hi def link i3ConfigBorderStyle Identifier +hi def link i3ConfigEdge Identifier +hi def link i3ConfigFloating Identifier +hi def link i3ConfigCommandKeyword Identifier +hi def link i3ConfigNoFocusKeyword Identifier +hi def link i3ConfigInitializeKeyword Identifier +hi def link i3ConfigAssignKeyword Identifier +hi def link i3ConfigResourceKeyword Identifier +hi def link i3ConfigExecKeyword Identifier +hi def link i3ConfigWorkspaceKeyword Identifier +hi def link i3ConfigClientColorKeyword Identifier +hi def link i3ConfigInterprocessKeyword Identifier +hi def link i3ConfigMouseWarpingKeyword Identifier +hi def link i3ConfigFocusFollowsMouseKeyword Identifier +hi def link i3ConfigPopupOnFullscreenKeyword Identifier +hi def link i3ConfigFocusWrappingKeyword Identifier +hi def link i3ConfigForceXineramaKeyword Identifier +hi def link i3ConfigAutomaticSwitchKeyword Identifier +hi def link i3ConfigDelayUrgencyKeyword Identifier +hi def link i3ConfigFocusOnActivationKeyword Identifier +hi def link i3ConfigDrawingMarksKeyword Identifier +hi def link i3ConfigBlockKeyword Identifier +hi def link i3ConfigVariable Statement +hi def link i3ConfigArbitraryCommand Type + +let b:current_syntax = "i3config" diff --git a/runtime/syntax/indent.vim b/runtime/syntax/indent.vim index ddeae67e0d..b2a1a0c85f 100644 --- a/runtime/syntax/indent.vim +++ b/runtime/syntax/indent.vim @@ -1,7 +1,8 @@ " Vim syntax file -" Language: indent(1) configuration file -" Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2010-01-23 +" Language: indent(1) configuration file +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2021 Nov 17 " indent_is_bsd: If exists, will change somewhat to match BSD implementation " " TODO: is the deny-all (a la lilo.vim nice or no?)... @@ -27,7 +28,7 @@ syn region indentComment start='//' skip='\\$' end='$' \ contains=indentTodo,@Spell if !exists("indent_is_bsd") - syn match indentOptions '-i\|--indentation-level\|-il\|--indent-level' + syn match indentOptions '-i\|--indent-level\|-il\|--indent-label' \ nextgroup=indentNumber skipwhite skipempty endif syn match indentOptions '-\%(bli\|c\%([bl]i\|[dip]\)\=\|di\=\|ip\=\|lc\=\|pp\=i\|sbi\|ts\|-\%(brace-indent\|comment-indentation\|case-brace-indentation\|declaration-comment-column\|continuation-indentation\|case-indentation\|else-endif-column\|line-comments-indentation\|declaration-indentation\|indent-level\|parameter-indentation\|line-length\|comment-line-length\|paren-indentation\|preprocessor-indentation\|struct-brace-indentation\|tab-size\)\)' diff --git a/runtime/syntax/lisp.vim b/runtime/syntax/lisp.vim index 434150ab26..90513e3a00 100644 --- a/runtime/syntax/lisp.vim +++ b/runtime/syntax/lisp.vim @@ -1,8 +1,8 @@ " Vim syntax file " Language: Lisp " Maintainer: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> -" Last Change: Jul 11, 2019 -" Version: 30 +" Last Change: Nov 10, 2021 +" Version: 31 " URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_LISP " " Thanks to F Xavier Noria for a list of 978 Common Lisp symbols taken from HyperSpec @@ -54,20 +54,20 @@ if exists("g:lisp_rainbow") && g:lisp_rainbow != 0 syn region lispParen8 contained matchgroup=hlLevel8 start="`\=(" end=")" skip="|.\{-}|" contains=@lispListCluster,lispParen9 syn region lispParen9 contained matchgroup=hlLevel9 start="`\=(" end=")" skip="|.\{-}|" contains=@lispListCluster,lispParen0 else - syn region lispList matchgroup=lispParen start="(" skip="|.\{-}|" matchgroup=lispParen end=")" contains=@lispListCluster - syn region lispBQList matchgroup=PreProc start="`(" skip="|.\{-}|" matchgroup=PreProc end=")" contains=@lispListCluster + syn region lispList matchgroup=lispParen start="(" skip="|.\{-}|" matchgroup=lispParen end=")" contains=@lispListCluster + syn region lispBQList matchgroup=PreProc start="`(" skip="|.\{-}|" matchgroup=PreProc end=")" contains=@lispListCluster endif " --------------------------------------------------------------------- " Atoms: {{{1 -syn match lispAtomMark "'" -syn match lispAtom "'("me=e-1 contains=lispAtomMark nextgroup=lispAtomList -syn match lispAtom "'[^ \t()]\+" contains=lispAtomMark -syn match lispAtomBarSymbol !'|..\{-}|! contains=lispAtomMark -syn region lispAtom start=+'"+ skip=+\\"+ end=+"+ -syn region lispAtomList contained matchgroup=Special start="(" skip="|.\{-}|" matchgroup=Special end=")" contains=@lispAtomCluster,lispString,lispEscapeSpecial -syn match lispAtomNmbr contained "\<\d\+" -syn match lispLeadWhite contained "^\s\+" +syn match lispAtomMark "'" +syn match lispAtom "'("me=e-1 contains=lispAtomMark nextgroup=lispAtomList +syn match lispAtom "'[^ \t()]\+" contains=lispAtomMark +syn match lispAtomBarSymbol !'|..\{-}|! contains=lispAtomMark +syn region lispAtom start=+'"+ skip=+\\"+ end=+"+ +syn region lispAtomList contained matchgroup=Special start="(" skip="|.\{-}|" matchgroup=Special end=")" contains=@lispAtomCluster,lispString,lispEscapeSpecial +syn match lispAtomNmbr contained "\<\d\+" +syn match lispLeadWhite contained "^\s\+" " --------------------------------------------------------------------- " Standard Lisp Functions and Macros: {{{1 @@ -553,6 +553,8 @@ syn match lispParenError ")" syn cluster lispCommentGroup contains=lispTodo,@Spell syn match lispComment ";.*$" contains=@lispCommentGroup syn region lispCommentRegion start="#|" end="|#" contains=lispCommentRegion,@lispCommentGroup +syn region lispComment start="#+nil" end="\ze)" contains=@lispCommentGroup +syn match lispComment '^\s*#+nil.*$' contains=@lispCommentGroup syn keyword lispTodo contained combak combak: todo todo: " --------------------------------------------------------------------- diff --git a/runtime/syntax/python.vim b/runtime/syntax/python.vim index 3427aa06c8..2293163a5b 100644 --- a/runtime/syntax/python.vim +++ b/runtime/syntax/python.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Python " Maintainer: Zvezdan Petkovic <zpetkovic@acm.org> -" Last Change: 2021 Feb 15 +" Last Change: 2021 Dec 10 " Credits: Neil Schemenauer <nas@python.ca> " Dmitry Vasiliev " @@ -77,13 +77,14 @@ endif " " The list can be checked using: " -" python3 -c 'import keyword, pprint; pprint.pprint(keyword.kwlist, compact=True)' +" python3 -c 'import keyword, pprint; pprint.pprint(keyword.kwlist + keyword.softkwlist, compact=True)' " syn keyword pythonStatement False None True syn keyword pythonStatement as assert break continue del global syn keyword pythonStatement lambda nonlocal pass return with yield syn keyword pythonStatement class def nextgroup=pythonFunction skipwhite syn keyword pythonConditional elif else if +syn keyword pythonConditional case match syn keyword pythonRepeat for while syn keyword pythonOperator and in is not or syn keyword pythonException except finally raise try diff --git a/runtime/syntax/routeros.vim b/runtime/syntax/routeros.vim new file mode 100644 index 0000000000..b6effc9b62 --- /dev/null +++ b/runtime/syntax/routeros.vim @@ -0,0 +1,91 @@ +" Vim syntax file +" Language: MikroTik RouterOS Script +" Maintainer: zainin <z@wintr.dev> +" Original Author: ndbjorne @ MikroTik forums +" Last Change: 2021 Nov 14 + +" quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +syn case ignore + +syn iskeyword @,48-57,- + +" comments +syn match routerosComment /^\s*\zs#.*/ + +" options submenus: /interface ether1 etc +syn match routerosSubMenu "\([a-z]\)\@<!/[a-zA-Z0-9-]*" + +" variables are matched by looking at strings ending with "=", e.g. var= +syn match routerosVariable "[a-zA-Z0-9-/]*\(=\)\@=" +syn match routerosVariable "$[a-zA-Z0-9-]*" + +" colored for clarity +syn match routerosDelimiter "[,=]" +" match slash in CIDR notation (1.2.3.4/24, 2001:db8::/48, ::1/128) +syn match routerosDelimiter "\(\x\|:\)\@<=\/\(\d\)\@=" +" dash in IP ranges +syn match routerosDelimiter "\(\x\|:\)\@<=-\(\x\|:\)\@=" + +" match service names after "set", like in original routeros syntax +syn match routerosService "\(set\)\@<=\s\(api-ssl\|api\|dns\|ftp\|http\|https\|pim\|ntp\|smb\|ssh\|telnet\|winbox\|www\|www-ssl\)" + +" colors various interfaces +syn match routerosInterface "bridge\d\+\|ether\d\+\|wlan\d\+\|pppoe-\(out\|in\)\d\+" + +syn keyword routerosBoolean yes no true false + +syn keyword routerosConditional if + +" operators +syn match routerosOperator " \zs[-+*<>=!~^&.,]\ze " +syn match routerosOperator "[<>!]=" +syn match routerosOperator "<<\|>>" +syn match routerosOperator "[+-]\d\@=" + +syn keyword routerosOperator and or in + +" commands +syn keyword routerosCommands beep delay put len typeof pick log time set find environment +syn keyword routerosCommands terminal error parse resolve toarray tobool toid toip toip6 +syn keyword routerosCommands tonum tostr totime add remove enable disable where get print +syn keyword routerosCommands export edit find append as-value brief detail count-only file +syn keyword routerosCommands follow follow-only from interval terse value-list without-paging +syn keyword routerosCommands return + +" variable types +syn keyword routerosType global local + +" loop keywords +syn keyword routerosRepeat do while for foreach + +syn match routerosSpecial "[():[\]{|}]" + +syn match routerosLineContinuation "\\$" + +syn match routerosEscape "\\["\\nrt$?_abfv]" contained display +syn match routerosEscape "\\\x\x" contained display + +syn region routerosString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=routerosEscape,routerosLineContinuation + +hi link routerosComment Comment +hi link routerosSubMenu Function +hi link routerosVariable Identifier +hi link routerosDelimiter Operator +hi link routerosEscape Special +hi link routerosService Type +hi link routerosInterface Type +hi link routerosBoolean Boolean +hi link routerosConditional Conditional +hi link routerosOperator Operator +hi link routerosCommands Operator +hi link routerosType Type +hi link routerosRepeat Repeat +hi link routerosSpecial Delimiter +hi link routerosString String +hi link routerosLineContinuation Special + +let b:current_syntax = "routeros" diff --git a/runtime/syntax/squirrel.vim b/runtime/syntax/squirrel.vim new file mode 100644 index 0000000000..81d59cc986 --- /dev/null +++ b/runtime/syntax/squirrel.vim @@ -0,0 +1,50 @@ +" Vim syntax file +" Language: squirrel +" Current Maintainer: Matt Dunford (zenmatic@gmail.com) +" URL: https://github.com/zenmatic/vim-syntax-squirrel +" Last Change: 2021 Nov 28 + +" http://squirrel-lang.org/ + +" quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +" inform C syntax that the file was included from cpp.vim +let b:filetype_in_cpp_family = 1 + +" Read the C syntax to start with +runtime! syntax/c.vim +unlet b:current_syntax + +" squirrel extensions +syn keyword squirrelStatement delete this in yield resume base clone +syn keyword squirrelAccess local +syn keyword cConstant null +syn keyword squirrelModifier static +syn keyword squirrelType bool instanceof typeof +syn keyword squirrelExceptions throw try catch +syn keyword squirrelStructure class function extends constructor +syn keyword squirrelBoolean true false +syn keyword squirrelRepeat foreach + +syn region squirrelMultiString start='@"' end='"$' end='";$'me=e-1 + +syn match squirrelShComment "^\s*#.*$" + +" Default highlighting +hi def link squirrelAccess squirrelStatement +hi def link squirrelExceptions Exception +hi def link squirrelStatement Statement +hi def link squirrelModifier Type +hi def link squirrelType Type +hi def link squirrelStructure Structure +hi def link squirrelBoolean Boolean +hi def link squirrelMultiString String +hi def link squirrelRepeat cRepeat +hi def link squirrelShComment Comment + +let b:current_syntax = "squirrel" + +" vim: ts=8 diff --git a/runtime/syntax/tcl.vim b/runtime/syntax/tcl.vim index 73b2b3fa11..59cb04f70f 100644 --- a/runtime/syntax/tcl.vim +++ b/runtime/syntax/tcl.vim @@ -6,8 +6,8 @@ " (previously Matt Neumann <mattneu@purpleturtle.com>) " (previously Allan Kelly <allan@fruitloaf.co.uk>) " Original: Robin Becker <robin@jessikat.demon.co.uk> -" Last Change: 2021 Oct 03 -" Version: 1.14 +" Last Change: 2021 Nov 16 +" Version: 1.14 plus improvements from PR #8948 " URL: (removed, no longer worked) " quit when a syntax file was already loaded @@ -192,18 +192,18 @@ syn region tcltkCommand matchgroup=tcltkCommandColor start="\<lsort\>" matchgrou syn keyword tclTodo contained TODO " Sequences which are backslash-escaped: http://www.tcl.tk/man/tcl8.5/TclCmd/Tcl.htm#M16 -" Octal, hexadecimal, unicode codepoints, and the classics. +" Octal, hexadecimal, Unicode codepoints, and the classics. " Tcl takes as many valid characters in a row as it can, so \xAZ in a string is newline followed by 'Z'. -syn match tclSpecial contained '\\\([0-7]\{1,3}\|x\x\{1,2}\|u\x\{1,4}\|[abfnrtv]\)' +syn match tclSpecial contained '\\\(\o\{1,3}\|x\x\{1,2}\|u\x\{1,4}\|[abfnrtv]\)' syn match tclSpecial contained '\\[\[\]\{\}\"\$]' " Command appearing inside another command or inside a string. syn region tclEmbeddedStatement start='\[' end='\]' contained contains=tclCommand,tclNumber,tclLineContinue,tclString,tclVarRef,tclEmbeddedStatement " A string needs the skip argument as it may legitimately contain \". " Match at start of line -syn region tclString start=+^"+ end=+"+ contains=@tclSpecialC skip=+\\\\\|\\"+ +syn region tclString start=+^"+ end=+"+ contains=@tclSpecialC,@Spell skip=+\\\\\|\\"+ "Match all other legal strings. -syn region tclString start=+[^\\]"+ms=s+1 end=+"+ contains=@tclSpecialC,@tclVarRefC,tclEmbeddedStatement skip=+\\\\\|\\"+ +syn region tclString start=+[^\\]"+ms=s+1 end=+"+ contains=@tclSpecialC,@tclVarRefC,tclEmbeddedStatement,@Spell skip=+\\\\\|\\"+ " Line continuation is backslash immediately followed by newline. syn match tclLineContinue '\\$' @@ -222,12 +222,12 @@ syn match tclNumber "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>" "floating point number, without dot, with exponent syn match tclNumber "\<\d\+e[-+]\=\d\+[fl]\=\>" "hex number -syn match tclNumber "0x[0-9a-f]\+\(u\=l\=\|lu\)\>" -"syn match tclIdentifier "\<[a-z_][a-z0-9_]*\>" +syn match tclNumber "0x\x\+\(u\=l\=\|lu\)\>" +"syn match tclIdentifier "\<\h\w*\>" syn case match -syn region tclComment start="^\s*\#" skip="\\$" end="$" contains=tclTodo -syn region tclComment start=/;\s*\#/hs=s+1 skip="\\$" end="$" contains=tclTodo +syn region tclComment start="^\s*\#" skip="\\$" end="$" contains=tclTodo,@Spell +syn region tclComment start=/;\s*\#/hs=s+1 skip="\\$" end="$" contains=tclTodo,@Spell "syn match tclComment /^\s*\#.*$/ "syn match tclComment /;\s*\#.*$/hs=s+1 diff --git a/runtime/syntax/texinfo.vim b/runtime/syntax/texinfo.vim index a4b7689707..79a4dfe821 100644 --- a/runtime/syntax/texinfo.vim +++ b/runtime/syntax/texinfo.vim @@ -1,396 +1,46 @@ " Vim syntax file -" Language: Texinfo (macro package for TeX) -" Maintainer: Sandor Kopanyi <sandor.kopanyi@mailbox.hu> -" URL: <-> -" Last Change: 2004 Jun 23 -" -" the file follows the Texinfo manual structure; this file is based -" on manual for Texinfo version 4.0, 28 September 1999 -" since @ can have special meanings, everything is 'match'-ed and 'region'-ed -" (including @ in 'iskeyword' option has unexpected effects) +" Language: Texinfo (documentation format) +" Maintainer: Robert Dodier <robert.dodier@gmail.com> +" Latest Revision: 2021-12-15 -" quit when a syntax file was already loaded if exists("b:current_syntax") finish endif -if !exists("main_syntax") - let main_syntax = 'texinfo' -endif - -"in Texinfo can be real big things, like tables; sync for that -syn sync lines=200 - -"some general stuff -"syn match texinfoError "\S" contained TODO -syn match texinfoIdent "\k\+" contained "IDENTifier -syn match texinfoAssignment "\k\+\s*=\s*\k\+\s*$" contained "assigment statement ( var = val ) -syn match texinfoSinglePar "\k\+\s*$" contained "single parameter (used for several @-commands) -syn match texinfoIndexPar "\k\k\s*$" contained "param. used for different *index commands (+ @documentlanguage command) - - -"marking words and phrases (chap. 9 in Texinfo manual) -"(almost) everything appears as 'contained' too; is for tables (@table) - -"this chapter is at the beginning of this file to avoid overwritings - -syn match texinfoSpecialChar "@acronym" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@acronym{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@b" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@b{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@cite" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@cite{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@code" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@code{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@command" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@command{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@dfn" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@dfn{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@email" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@email{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@emph" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@emph{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@env" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@env{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@file" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@file{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@i" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@i{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@kbd" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@kbd{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@key" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@key{" end="}" contains=texinfoSpecialChar -syn match texinfoSpecialChar "@option" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@option{" end="}" contains=texinfoSpecialChar -syn match texinfoSpecialChar "@r" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@r{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@samp" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@samp{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@sc" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@sc{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@strong" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@strong{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@t" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@t{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@url" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@url{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoSpecialChar "@var" contained -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@var{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn match texinfoAtCmd "^@kbdinputstyle" nextgroup=texinfoSinglePar skipwhite - - -"overview of Texinfo (chap. 1 in Texinfo manual) -syn match texinfoComment "@c .*" -syn match texinfoComment "@c$" -syn match texinfoComment "@comment .*" -syn region texinfoMltlnAtCmd matchgroup=texinfoComment start="^@ignore\s*$" end="^@end ignore\s*$" contains=ALL - - -"beginning a Texinfo file (chap. 3 in Texinfo manual) -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="@center " skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd oneline -syn region texinfoMltlnDMAtCmd matchgroup=texinfoAtCmd start="^@detailmenu\s*$" end="^@end detailmenu\s*$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@setfilename " skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@settitle " skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@shorttitlepage " skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@title " skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@titlefont{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@titlepage\s*$" end="^@end titlepage\s*$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoMltlnDMAtCmd,texinfoAtCmd,texinfoPrmAtCmd,texinfoMltlnAtCmd -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@vskip " skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn match texinfoAtCmd "^@exampleindent" nextgroup=texinfoSinglePar skipwhite -syn match texinfoAtCmd "^@headings" nextgroup=texinfoSinglePar skipwhite -syn match texinfoAtCmd "^\\input" nextgroup=texinfoSinglePar skipwhite -syn match texinfoAtCmd "^@paragraphindent" nextgroup=texinfoSinglePar skipwhite -syn match texinfoAtCmd "^@setchapternewpage" nextgroup=texinfoSinglePar skipwhite - - -"ending a Texinfo file (chap. 4 in Texinfo manual) -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="@author " skip="\\$" end="$" contains=texinfoSpecialChar oneline -"all below @bye should be comment TODO -syn match texinfoAtCmd "^@bye\s*$" -syn match texinfoAtCmd "^@contents\s*$" -syn match texinfoAtCmd "^@printindex" nextgroup=texinfoIndexPar skipwhite -syn match texinfoAtCmd "^@setcontentsaftertitlepage\s*$" -syn match texinfoAtCmd "^@setshortcontentsaftertitlepage\s*$" -syn match texinfoAtCmd "^@shortcontents\s*$" -syn match texinfoAtCmd "^@summarycontents\s*$" - - -"chapter structuring (chap. 5 in Texinfo manual) -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@appendix" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@appendixsec" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@appendixsection" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@appendixsubsec" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@appendixsubsubsec" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@centerchap" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@chapheading" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@chapter" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@heading" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@majorheading" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@section" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@subheading " skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@subsection" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@subsubheading" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@subsubsection" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@subtitle" skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@unnumbered" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@unnumberedsec" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@unnumberedsubsec" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@unnumberedsubsubsec" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn match texinfoAtCmd "^@lowersections\s*$" -syn match texinfoAtCmd "^@raisesections\s*$" - - -"nodes (chap. 6 in Texinfo manual) -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@anchor{" end="}" -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@top" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@node" skip="\\$" end="$" contains=texinfoSpecialChar oneline - - -"menus (chap. 7 in Texinfo manual) -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@menu\s*$" end="^@end menu\s*$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoMltlnDMAtCmd - - -"cross references (chap. 8 in Texinfo manual) -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@inforef{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@pxref{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@ref{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@uref{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@xref{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd - - -"marking words and phrases (chap. 9 in Texinfo manual) -"(almost) everything appears as 'contained' too; is for tables (@table) - -"this chapter is at the beginning of this file to avoid overwritings - - -"quotations and examples (chap. 10 in Texinfo manual) -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@cartouche\s*$" end="^@end cartouche\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@display\s*$" end="^@end display\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@example\s*$" end="^@end example\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@flushleft\s*$" end="^@end flushleft\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@flushright\s*$" end="^@end flushright\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@format\s*$" end="^@end format\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@lisp\s*$" end="^@end lisp\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@quotation\s*$" end="^@end quotation\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@smalldisplay\s*$" end="^@end smalldisplay\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@smallexample\s*$" end="^@end smallexample\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@smallformat\s*$" end="^@end smallformat\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@smalllisp\s*$" end="^@end smalllisp\s*$" contains=ALL -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@exdent" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn match texinfoAtCmd "^@noindent\s*$" -syn match texinfoAtCmd "^@smallbook\s*$" - - -"lists and tables (chap. 11 in Texinfo manual) -syn match texinfoAtCmd "@asis" contained -syn match texinfoAtCmd "@columnfractions" contained -syn match texinfoAtCmd "@item" contained -syn match texinfoAtCmd "@itemx" contained -syn match texinfoAtCmd "@tab" contained -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@enumerate" end="^@end enumerate\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ftable" end="^@end ftable\s*$" contains=ALL -syn region texinfoMltlnNAtCmd matchgroup=texinfoAtCmd start="^@itemize" end="^@end itemize\s*$" contains=ALL -syn region texinfoMltlnNAtCmd matchgroup=texinfoAtCmd start="^@multitable" end="^@end multitable\s*$" contains=ALL -syn region texinfoMltlnNAtCmd matchgroup=texinfoAtCmd start="^@table" end="^@end table\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@vtable" end="^@end vtable\s*$" contains=ALL +let s:cpo_save = &cpo +set cpo&vim +syn match texinfoControlSequence display '\(@end [a-zA-Z@]\+\|@[a-zA-Z@]\+\)' -"indices (chap. 12 in Texinfo manual) -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@\(c\|f\|k\|p\|t\|v\)index" skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@..index" skip="\\$" end="$" contains=texinfoSpecialChar oneline -"@defcodeindex and @defindex is defined after chap. 15's @def* commands (otherwise those ones will overwrite these ones) -syn match texinfoSIPar "\k\k\s*\k\k\s*$" contained -syn match texinfoAtCmd "^@syncodeindex" nextgroup=texinfoSIPar skipwhite -syn match texinfoAtCmd "^@synindex" nextgroup=texinfoSIPar skipwhite +syn match texinfoComment display '^\s*\(@comment\|@c\)\>.*$' -"special insertions (chap. 13 in Texinfo manual) -syn match texinfoSpecialChar "@\(!\|?\|@\|\s\)" -syn match texinfoSpecialChar "@{" -syn match texinfoSpecialChar "@}" -"accents -syn match texinfoSpecialChar "@=." -syn match texinfoSpecialChar "@\('\|\"\|\^\|`\)[aeiouyAEIOUY]" -syn match texinfoSpecialChar "@\~[aeinouyAEINOUY]" -syn match texinfoSpecialChar "@dotaccent{.}" -syn match texinfoSpecialChar "@H{.}" -syn match texinfoSpecialChar "@,{[cC]}" -syn match texinfoSpecialChar "@AA{}" -syn match texinfoSpecialChar "@aa{}" -syn match texinfoSpecialChar "@L{}" -syn match texinfoSpecialChar "@l{}" -syn match texinfoSpecialChar "@O{}" -syn match texinfoSpecialChar "@o{}" -syn match texinfoSpecialChar "@ringaccent{.}" -syn match texinfoSpecialChar "@tieaccent{..}" -syn match texinfoSpecialChar "@u{.}" -syn match texinfoSpecialChar "@ubaraccent{.}" -syn match texinfoSpecialChar "@udotaccent{.}" -syn match texinfoSpecialChar "@v{.}" -"ligatures -syn match texinfoSpecialChar "@AE{}" -syn match texinfoSpecialChar "@ae{}" -syn match texinfoSpecialChar "@copyright{}" -syn match texinfoSpecialChar "@bullet" contained "for tables and lists -syn match texinfoSpecialChar "@bullet{}" -syn match texinfoSpecialChar "@dotless{i}" -syn match texinfoSpecialChar "@dotless{j}" -syn match texinfoSpecialChar "@dots{}" -syn match texinfoSpecialChar "@enddots{}" -syn match texinfoSpecialChar "@equiv" contained "for tables and lists -syn match texinfoSpecialChar "@equiv{}" -syn match texinfoSpecialChar "@error{}" -syn match texinfoSpecialChar "@exclamdown{}" -syn match texinfoSpecialChar "@expansion{}" -syn match texinfoSpecialChar "@minus" contained "for tables and lists -syn match texinfoSpecialChar "@minus{}" -syn match texinfoSpecialChar "@OE{}" -syn match texinfoSpecialChar "@oe{}" -syn match texinfoSpecialChar "@point" contained "for tables and lists -syn match texinfoSpecialChar "@point{}" -syn match texinfoSpecialChar "@pounds{}" -syn match texinfoSpecialChar "@print{}" -syn match texinfoSpecialChar "@questiondown{}" -syn match texinfoSpecialChar "@result" contained "for tables and lists -syn match texinfoSpecialChar "@result{}" -syn match texinfoSpecialChar "@ss{}" -syn match texinfoSpecialChar "@TeX{}" -"other -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@dmn{" end="}" -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@footnote{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@image{" end="}" -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@math{" end="}" -syn match texinfoAtCmd "@footnotestyle" nextgroup=texinfoSinglePar skipwhite +syn region texinfoCode matchgroup=texinfoControlSequence start="@code{" end="}" contains=ALL +syn region texinfoVerb matchgroup=texinfoControlSequence start="@verb{" end="}" contains=ALL +syn region texinfoArgument matchgroup=texinfoBrace start="{" end="}" contains=ALLBUT -"making and preventing breaks (chap. 14 in Texinfo manual) -syn match texinfoSpecialChar "@\(\*\|-\|\.\)" -syn match texinfoAtCmd "^@need" nextgroup=texinfoSinglePar skipwhite -syn match texinfoAtCmd "^@page\s*$" -syn match texinfoAtCmd "^@sp" nextgroup=texinfoSinglePar skipwhite -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@group\s*$" end="^@end group\s*$" contains=ALL -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@hyphenation{" end="}" -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@w{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd +syn region texinfoExample matchgroup=texinfoControlSequence start="^@example\s*$" end="^@end example\s*$" contains=ALL +syn region texinfoVerbatim matchgroup=texinfoControlSequence start="^@verbatim\s*$" end="^@end verbatim\s*$" -"definition commands (chap. 15 in Texinfo manual) -syn match texinfoMltlnAtCmdFLine "^@def\k\+" contained -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@def\k\+" end="^@end def\k\+$" contains=ALL - -"next 2 commands are from chap. 12; must be defined after @def* commands above to overwrite them -syn match texinfoAtCmd "@defcodeindex" nextgroup=texinfoIndexPar skipwhite -syn match texinfoAtCmd "@defindex" nextgroup=texinfoIndexPar skipwhite - - -"conditionally visible text (chap. 16 in Texinfo manual) -syn match texinfoAtCmd "^@clear" nextgroup=texinfoSinglePar skipwhite -syn region texinfoMltln2AtCmd matchgroup=texinfoAtCmd start="^@html\s*$" end="^@end html\s*$" -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ifclear" end="^@end ifclear\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ifhtml" end="^@end ifhtml\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ifinfo" end="^@end ifinfo\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ifnothtml" end="^@end ifnothtml\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ifnotinfo" end="^@end ifnotinfo\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ifnottex" end="^@end ifnottex\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@ifset" end="^@end ifset\s*$" contains=ALL -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@iftex" end="^@end iftex\s*$" contains=ALL -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@set " skip="\\$" end="$" contains=texinfoSpecialChar oneline -syn region texinfoTexCmd start="\$\$" end="\$\$" contained -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@tex" end="^@end tex\s*$" contains=texinfoTexCmd -syn region texinfoBrcPrmAtCmd matchgroup=texinfoAtCmd start="@value{" end="}" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd - - -"internationalization (chap. 17 in Texinfo manual) -syn match texinfoAtCmd "@documentencoding" nextgroup=texinfoSinglePar skipwhite -syn match texinfoAtCmd "@documentlanguage" nextgroup=texinfoIndexPar skipwhite - - -"defining new texinfo commands (chap. 18 in Texinfo manual) -syn match texinfoAtCmd "@alias" nextgroup=texinfoAssignment skipwhite -syn match texinfoDIEPar "\S*\s*,\s*\S*\s*,\s*\S*\s*$" contained -syn match texinfoAtCmd "@definfoenclose" nextgroup=texinfoDIEPar skipwhite -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@macro" end="^@end macro\s*$" contains=ALL - - -"formatting hardcopy (chap. 19 in Texinfo manual) -syn match texinfoAtCmd "^@afourlatex\s*$" -syn match texinfoAtCmd "^@afourpaper\s*$" -syn match texinfoAtCmd "^@afourwide\s*$" -syn match texinfoAtCmd "^@finalout\s*$" -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@pagesizes" end="$" oneline - - -"creating and installing Info Files (chap. 20 in Texinfo manual) -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@dircategory" skip="\\$" end="$" oneline -syn region texinfoMltlnAtCmd matchgroup=texinfoAtCmd start="^@direntry\s*$" end="^@end direntry\s*$" contains=texinfoSpecialChar -syn match texinfoAtCmd "^@novalidate\s*$" - - -"include files (appendix E in Texinfo manual) -syn match texinfoAtCmd "^@include" nextgroup=texinfoSinglePar skipwhite - - -"page headings (appendix F in Texinfo manual) -syn match texinfoHFSpecialChar "@|" contained -syn match texinfoThisAtCmd "@thischapter" contained -syn match texinfoThisAtCmd "@thischaptername" contained -syn match texinfoThisAtCmd "@thisfile" contained -syn match texinfoThisAtCmd "@thispage" contained -syn match texinfoThisAtCmd "@thistitle" contained -syn match texinfoThisAtCmd "@today{}" contained -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@evenfooting" skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoThisAtCmd,texinfoHFSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@evenheading" skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoThisAtCmd,texinfoHFSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@everyfooting" skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoThisAtCmd,texinfoHFSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@everyheading" skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoThisAtCmd,texinfoHFSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@oddfooting" skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoThisAtCmd,texinfoHFSpecialChar oneline -syn region texinfoPrmAtCmd matchgroup=texinfoAtCmd start="^@oddheading" skip="\\$" end="$" contains=texinfoSpecialChar,texinfoBrcPrmAtCmd,texinfoThisAtCmd,texinfoHFSpecialChar oneline - - -"refilling paragraphs (appendix H in Texinfo manual) -syn match texinfoAtCmd "@refill" - - -syn cluster texinfoAll contains=ALLBUT,texinfoThisAtCmd,texinfoHFSpecialChar -syn cluster texinfoReducedAll contains=texinfoSpecialChar,texinfoBrcPrmAtCmd -"============================================================================== -" highlighting - -" Only when an item doesn't have highlighting yet - -hi def link texinfoSpecialChar Special -hi def link texinfoHFSpecialChar Special - -hi def link texinfoError Error -hi def link texinfoIdent Identifier -hi def link texinfoAssignment Identifier -hi def link texinfoSinglePar Identifier -hi def link texinfoIndexPar Identifier -hi def link texinfoSIPar Identifier -hi def link texinfoDIEPar Identifier -hi def link texinfoTexCmd PreProc - - -hi def link texinfoAtCmd Statement "@-command -hi def link texinfoPrmAtCmd String "@-command in one line with unknown nr. of parameters - "is String because is found as a region and is 'matchgroup'-ed - "to texinfoAtCmd -hi def link texinfoBrcPrmAtCmd String "@-command with parameter(s) in braces ({}) - "is String because is found as a region and is 'matchgroup'-ed to texinfoAtCmd -hi def link texinfoMltlnAtCmdFLine texinfoAtCmd "repeated embedded First lines in @-commands -hi def link texinfoMltlnAtCmd String "@-command in multiple lines - "is String because is found as a region and is 'matchgroup'-ed to texinfoAtCmd -hi def link texinfoMltln2AtCmd PreProc "@-command in multiple lines (same as texinfoMltlnAtCmd, just with other colors) -hi def link texinfoMltlnDMAtCmd PreProc "@-command in multiple lines (same as texinfoMltlnAtCmd, just with other colors; used for @detailmenu, which can be included in @menu) -hi def link texinfoMltlnNAtCmd Normal "@-command in multiple lines (same as texinfoMltlnAtCmd, just with other colors) -hi def link texinfoThisAtCmd Statement "@-command used in headers and footers (@this... series) - -hi def link texinfoComment Comment +syn region texinfoMenu matchgroup=texinfoControlSequence start="^@menu\s*$" end="^@end menu\s*$" +if exists("g:texinfo_delimiters") + syn match texinfoDelimiter display '[][{}]' +endif +hi def link texinfoDelimiter Delimiter +hi def link texinfoComment Comment +hi def link texinfoControlSequence Identifier +hi def link texinfoBrace Operator +hi def link texinfoArgument Special +hi def link texinfoExample String +hi def link texinfoVerbatim String +hi def link texinfoVerb String +hi def link texinfoCode String +hi def link texinfoMenu String let b:current_syntax = "texinfo" -if main_syntax == 'texinfo' - unlet main_syntax -endif - -" vim: ts=8 +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/syntax/vb.vim b/runtime/syntax/vb.vim index 8ddb1efac3..607f6130ba 100644 --- a/runtime/syntax/vb.vim +++ b/runtime/syntax/vb.vim @@ -1,9 +1,11 @@ " Vim syntax file -" Language: Visual Basic -" Maintainer: Tim Chase <vb.vim@tim.thechases.com> -" Former Maintainer: Robert M. Cortopassi <cortopar@mindspring.com> -" (tried multiple times to contact, but email bounced) +" Language: Visual Basic +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Former Maintainer: Tim Chase <vb.vim@tim.thechases.com> +" Former Maintainer: Robert M. Cortopassi <cortopar@mindspring.com> +" (tried multiple times to contact, but email bounced) " Last Change: +" 2021 Nov 26 Incorporated additions from Doug Kearns " 2005 May 25 Synched with work by Thomas Barthel " 2004 May 30 Added a few keywords @@ -13,7 +15,7 @@ " quit when a syntax file was already loaded if exists("b:current_syntax") - finish + finish endif " VB is case insensitive @@ -233,7 +235,7 @@ syn keyword vbKeyword Public PublicNotCreateable OnNewProcessSingleUse syn keyword vbKeyword InSameProcessMultiUse GlobalMultiUse Resume Seek syn keyword vbKeyword Set Static Step String Time WithEvents -syn keyword vbTodo contained TODO +syn keyword vbTodo contained TODO "Datatypes syn keyword vbTypes Boolean Byte Currency Date Decimal Double Empty @@ -319,46 +321,54 @@ syn match vbNumber "\<\d\+\>" syn match vbNumber "\<\d\+\.\d*\>" "floating point number, starting with a dot syn match vbNumber "\.\d\+\>" -"syn match vbNumber "{[[:xdigit:]-]\+}\|&[hH][[:xdigit:]]\+&" -"syn match vbNumber ":[[:xdigit:]]\+" -"syn match vbNumber "[-+]\=\<\d\+\>" -syn match vbFloat "[-+]\=\<\d\+[eE][\-+]\=\d\+" -syn match vbFloat "[-+]\=\<\d\+\.\d*\([eE][\-+]\=\d\+\)\=" -syn match vbFloat "[-+]\=\<\.\d\+\([eE][\-+]\=\d\+\)\=" +"syn match vbNumber "{[[:xdigit:]-]\+}\|&[hH][[:xdigit:]]\+&" +"syn match vbNumber ":[[:xdigit:]]\+" +"syn match vbNumber "[-+]\=\<\d\+\>" +syn match vbFloat "[-+]\=\<\d\+[eE][\-+]\=\d\+" +syn match vbFloat "[-+]\=\<\d\+\.\d*\([eE][\-+]\=\d\+\)\=" +syn match vbFloat "[-+]\=\<\.\d\+\([eE][\-+]\=\d\+\)\=" -" String and Character contstants +" String and Character constants syn region vbString start=+"+ end=+"\|$+ syn region vbComment start="\(^\|\s\)REM\s" end="$" contains=vbTodo syn region vbComment start="\(^\|\s\)\'" end="$" contains=vbTodo -syn match vbLineNumber "^\d\+\(\s\|$\)" -syn match vbTypeSpecifier "[a-zA-Z0-9][\$%&!#]"ms=s+1 +syn match vbLineLabel "^\h\w\+:" +syn match vbLineNumber "^\d\+\(:\|\s\|$\)" +syn match vbTypeSpecifier "\<\a\w*[@\$%&!#]"ms=s+1 syn match vbTypeSpecifier "#[a-zA-Z0-9]"me=e-1 +" Conditional Compilation +syn match vbPreProc "^#const\>" +syn region vbPreProc matchgroup=PreProc start="^#if\>" end="\<then\>" transparent contains=TOP +syn region vbPreProc matchgroup=PreProc start="^#elseif\>" end="\<then\>" transparent contains=TOP +syn match vbPreProc "^#else\>" +syn match vbPreProc "^#end\s*if\>" " Define the default highlighting. " Only when an item doesn't have highlighting yet -hi def link vbBoolean Boolean -hi def link vbLineNumber Comment -hi def link vbComment Comment -hi def link vbConditional Conditional -hi def link vbConst Constant -hi def link vbDefine Constant -hi def link vbError Error -hi def link vbFunction Identifier -hi def link vbIdentifier Identifier -hi def link vbNumber Number -hi def link vbFloat Float -hi def link vbMethods PreProc -hi def link vbOperator Operator -hi def link vbRepeat Repeat -hi def link vbString String -hi def link vbStatement Statement -hi def link vbKeyword Statement -hi def link vbEvents Special -hi def link vbTodo Todo -hi def link vbTypes Type -hi def link vbTypeSpecifier Type - +hi def link vbBoolean Boolean +hi def link vbLineNumber Comment +hi def link vbLineLabel Comment +hi def link vbComment Comment +hi def link vbConditional Conditional +hi def link vbConst Constant +hi def link vbDefine Constant +hi def link vbError Error +hi def link vbFunction Identifier +hi def link vbIdentifier Identifier +hi def link vbNumber Number +hi def link vbFloat Float +hi def link vbMethods PreProc +hi def link vbOperator Operator +hi def link vbRepeat Repeat +hi def link vbString String +hi def link vbStatement Statement +hi def link vbKeyword Statement +hi def link vbEvents Special +hi def link vbTodo Todo +hi def link vbTypes Type +hi def link vbTypeSpecifier Type +hi def link vbPreProc PreProc let b:current_syntax = "vb" diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 67756fa320..41993b65b0 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -53,7 +53,7 @@ syn case ignore syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo " Default highlighting groups {{{2 -syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineNr DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu +syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineFold CursorLineNr CursorLineSign DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu syn match vimHLGroup contained "Conceal" syn keyword vimOnlyHLGroup contained LineNrAbove LineNrBelow StatusLineTerm Terminal VisualNOS syn keyword nvimHLGroup contained Substitute TermCursor TermCursorNC @@ -161,10 +161,13 @@ syn match vimFBVar contained "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>" syn keyword vimCommand contained in " Insertions And Appends: insert append {{{2 +" (buftype != nofile test avoids having append, change, insert show up in the command window) " ======================= -syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=a\%[ppend]$" matchgroup=vimCommand end="^\.$"" -syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=c\%[hange]$" matchgroup=vimCommand end="^\.$"" -syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=i\%[nsert]$" matchgroup=vimCommand end="^\.$"" +if &buftype != 'nofile' + syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=a\%[ppend]$" matchgroup=vimCommand end="^\.$"" + syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=c\%[hange]$" matchgroup=vimCommand end="^\.$"" + syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=i\%[nsert]$" matchgroup=vimCommand end="^\.$"" +endif " Behave! {{{2 " ======= @@ -738,10 +741,10 @@ if g:vimsyn_embed =~# 'P' && filereadable(s:pythonpath) unlet! b:current_syntax syn cluster vimFuncBodyList add=vimPythonRegion exe "syn include @vimPythonScript ".s:pythonpath - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\s*<<\s*\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\=\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*\%(trim\s*\)\=\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*\%(trim\s*\)\=$+ end=+\.$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\s*<<\s*\%(trim\s*\)\=\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\=\s*<<\s*\%(trim\s*\)\=$+ end=+\.$+ contains=@vimPythonScript syn cluster vimFuncBodyList add=vimPythonRegion else syn region vimEmbedError start=+py\%[thon]3\=\s*<<\s*\z(.*\)$+ end=+^\z1$+ diff --git a/runtime/tools/check_colors.vim b/runtime/tools/check_colors.vim index 966072c706..85df882d1e 100644 --- a/runtime/tools/check_colors.vim +++ b/runtime/tools/check_colors.vim @@ -226,7 +226,13 @@ fu! Result(err) endif endfu -call Test_check_colors() - -let &cpo = s:save_cpo -unlet s:save_cpo +try + call Test_check_colors() +catch + echohl ErrorMsg + echomsg v:exception + echohl NONE +finally + let &cpo = s:save_cpo + unlet s:save_cpo +endtry diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 2d2d46d1bd..89fc14121e 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -162,6 +162,7 @@ CONFIG = { 'buf.lua', 'diagnostic.lua', 'codelens.lua', + 'tagfunc.lua', 'handlers.lua', 'util.lua', 'log.lua', diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua index c30a1b10da..0a7da4d4ef 100644 --- a/scripts/lintcommit.lua +++ b/scripts/lintcommit.lua @@ -47,7 +47,7 @@ end local function validate_commit(commit_message) local commit_split = vim.split(commit_message, ":") - -- Return true if the type is vim-patch since most of the normal rules don't + -- Return nil if the type is vim-patch since most of the normal rules don't -- apply. if commit_split[1] == "vim-patch" then return nil diff --git a/src/mpack/object.h b/src/mpack/object.h index 5327e56e18..e69821f9de 100644 --- a/src/mpack/object.h +++ b/src/mpack/object.h @@ -22,7 +22,7 @@ enum { }; /* Storing integer in pointers in undefined behavior according to the C - * standard. Define a union type to accomodate arbitrary user data associated + * standard. Define a union type to accommodate arbitrary user data associated * with nodes(and with requests in rpc.h). */ typedef union { void *p; diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 185d55daed..9c4b778169 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -326,7 +326,9 @@ add_custom_command( add_custom_command( OUTPUT ${VIM_MODULE_FILE} - COMMAND ${LUA_PRG} ${CHAR_BLOB_GENERATOR} ${VIM_MODULE_FILE} + COMMAND ${CMAKE_COMMAND} -E env + "LUAC_PRG=${LUAC_PRG}" + ${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE} ${LUA_VIM_MODULE_SOURCE} vim_module ${LUA_SHARED_MODULE_SOURCE} shared_module ${LUA_INSPECT_MODULE_SOURCE} inspect_module @@ -339,6 +341,7 @@ add_custom_command( ${LUA_INSPECT_MODULE_SOURCE} ${LUA_F_MODULE_SOURCE} ${LUA_META_MODULE_SOURCE} + VERBATIM ) list(APPEND NVIM_GENERATED_SOURCES @@ -468,9 +471,11 @@ list(APPEND NVIM_LINK_LIBRARIES if(UNIX) list(APPEND NVIM_LINK_LIBRARIES - m - util - ) + m) + if (NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS") + list(APPEND NVIM_LINK_LIBRARIES + util) + endif() endif() set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES}) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 4076a0d220..7988bff25a 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -29,7 +29,6 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/undo.h" @@ -535,7 +534,7 @@ end: /// @param channel_id /// @param buffer Buffer handle, or 0 for current buffer /// @param start_row First line index -/// @param start_column Last column +/// @param start_column First column /// @param end_row Last line index /// @param end_column Last column /// @param replacement Array of lines to use as replacement @@ -1246,7 +1245,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// If the current window already shows "buffer", the window is not switched /// If a window inside the current tabpage (including a float) already shows the /// buffer One of these windows will be set as current window temporarily. -/// Otherwise a temporary scratch window (calleed the "autocmd window" for +/// Otherwise a temporary scratch window (called the "autocmd window" for /// historical reasons) will be used. /// /// This is useful e.g. to call vimL functions that only work with the current diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 6f1fb15dac..742b953c2a 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -339,7 +339,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// @param col Column where to place the mark, 0-based. |api-indexing| /// @param opts Optional parameters. /// - id : id of the extmark to edit. -/// - end_line : ending line of the mark, 0-based inclusive. +/// - end_row : ending line of the mark, 0-based inclusive. /// - end_col : ending col of the mark, 0-based exclusive. /// - hl_group : name of the highlight group used to highlight /// this mark. @@ -431,16 +431,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } int line2 = -1; - if (opts->end_line.type == kObjectTypeInteger) { - Integer val = opts->end_line.data.integer; + + // For backward compatibility we support "end_line" as an alias for "end_row" + if (HAS_KEY(opts->end_line)) { + if (HAS_KEY(opts->end_row)) { + api_set_error(err, kErrorTypeValidation, "cannot use both end_row and end_line"); + goto error; + } + opts->end_row = opts->end_line; + } + + if (opts->end_row.type == kObjectTypeInteger) { + Integer val = opts->end_row.data.integer; if (val < 0 || val > buf->b_ml.ml_line_count) { - api_set_error(err, kErrorTypeValidation, "end_line value outside range"); + api_set_error(err, kErrorTypeValidation, "end_row value outside range"); goto error; } else { line2 = (int)val; } - } else if (HAS_KEY(opts->end_line)) { - api_set_error(err, kErrorTypeValidation, "end_line is not an integer"); + } else if (HAS_KEY(opts->end_row)) { + api_set_error(err, kErrorTypeValidation, "end_row is not an integer"); goto error; } @@ -571,10 +581,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer OPTION_TO_BOOL(right_gravity, right_gravity, true); // Only error out if they try to set end_right_gravity without - // setting end_col or end_line + // setting end_col or end_row if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) { api_set_error(err, kErrorTypeValidation, - "cannot set end_right_gravity without setting end_line or end_col"); + "cannot set end_right_gravity without setting end_row or end_col"); goto error; } @@ -839,7 +849,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// - on_win: called when starting to redraw a specific window. /// ["win", winid, bufnr, topline, botline_guess] /// - on_line: called for each buffer line being redrawn. (The -/// interation with fold lines is subject to change) +/// interaction with fold lines is subject to change) /// ["win", winid, bufnr, row] /// - on_end: called at the end of a redraw cycle /// ["end", tick] diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 144c252687..f3e7f2f1dc 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -5,6 +5,7 @@ return { set_extmark = { "id"; "end_line"; + "end_row"; "end_col"; "hl_group"; "virt_text"; @@ -58,5 +59,8 @@ return { "highlights"; "use_tabline"; }; + option = { + "scope"; + }; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d470def277..9b407eab8b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -513,7 +513,7 @@ String cbuf_to_string(const char *buf, size_t size) String cstrn_to_string(const char *str, size_t maxsize) FUNC_ATTR_NONNULL_ALL { - return cbuf_to_string(str, strnlen(str, maxsize)); + return cbuf_to_string(str, STRNLEN(str, maxsize)); } /// Creates a String using the given C string. Unlike diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index c1374ff00e..4f7c320129 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -71,7 +71,7 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err) FUNC_API_SINCE(3) { Dictionary result = ARRAY_DICT_INIT; - int id = syn_name2id((const char_u *)name.data); + int id = syn_name2id(name.data); if (id == 0) { api_set_error(err, kErrorTypeException, "Invalid highlight name: %s", @@ -642,7 +642,7 @@ void nvim_set_vvar(String name, Object value, Error *err) dict_set_var(&vimvardict, name, value, false, false, err); } -/// Gets an option value string. +/// Gets the global value of an option. /// /// @param name Option name /// @param[out] err Error details, if any @@ -653,6 +653,115 @@ Object nvim_get_option(String name, Error *err) return get_option_from(NULL, SREQ_GLOBAL, name, err); } +/// Gets the value of an option. The behavior of this function matches that of +/// |:set|: the local value of an option is returned if it exists; otherwise, +/// the global value is returned. Local values always correspond to the current +/// buffer or window. To get a buffer-local or window-local option for a +/// specific buffer or window, use |nvim_buf_get_option()| or +/// |nvim_win_get_option()|. +/// +/// @param name Option name +/// @param opts Optional parameters +/// - scope: One of 'global' or 'local'. Analagous to +/// |:setglobal| and |:setlocal|, respectively. +/// @param[out] err Error details, if any +/// @return Option value +Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) + FUNC_API_SINCE(9) +{ + Object rv = OBJECT_INIT; + + int scope = 0; + if (opts->scope.type == kObjectTypeString) { + if (!strcmp(opts->scope.data.string.data, "local")) { + scope = OPT_LOCAL; + } else if (!strcmp(opts->scope.data.string.data, "global")) { + scope = OPT_GLOBAL; + } else { + api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); + goto end; + } + } else if (HAS_KEY(opts->scope)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); + goto end; + } + + long numval = 0; + char *stringval = NULL; + switch (get_option_value(name.data, &numval, (char_u **)&stringval, scope)) { + case 0: + rv = STRING_OBJ(cstr_as_string(stringval)); + break; + case 1: + rv = INTEGER_OBJ(numval); + break; + case 2: + rv = BOOLEAN_OBJ(!!numval); + break; + default: + api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data); + goto end; + } + +end: + return rv; +} + +/// Sets the value of an option. The behavior of this function matches that of +/// |:set|: for global-local options, both the global and local value are set +/// unless otherwise specified with {scope}. +/// +/// @param name Option name +/// @param value New option value +/// @param opts Optional parameters +/// - scope: One of 'global' or 'local'. Analagous to +/// |:setglobal| and |:setlocal|, respectively. +/// @param[out] err Error details, if any +void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err) + FUNC_API_SINCE(9) +{ + int scope = 0; + if (opts->scope.type == kObjectTypeString) { + if (!strcmp(opts->scope.data.string.data, "local")) { + scope = OPT_LOCAL; + } else if (!strcmp(opts->scope.data.string.data, "global")) { + scope = OPT_GLOBAL; + } else { + api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); + return; + } + } else if (HAS_KEY(opts->scope)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); + return; + } + + long numval = 0; + char *stringval = NULL; + + switch (value.type) { + case kObjectTypeInteger: + numval = value.data.integer; + break; + case kObjectTypeBoolean: + numval = value.data.boolean ? 1 : 0; + break; + case kObjectTypeString: + stringval = value.data.string.data; + break; + case kObjectTypeNil: + // Do nothing + break; + default: + api_set_error(err, kErrorTypeValidation, "invalid value for option"); + return; + } + + char *e = set_option_value(name.data, numval, stringval, scope); + if (e) { + api_set_error(err, kErrorTypeException, "%s", e); + } +} + /// Gets the option information for all options. /// /// The dictionary has the full option names as keys and option metadata @@ -694,7 +803,7 @@ Dictionary nvim_get_option_info(String name, Error *err) return get_vimoption(name, err); } -/// Sets an option value. +/// Sets the global value of an option. /// /// @param channel_id /// @param name Option name @@ -733,7 +842,7 @@ void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err) for (uint32_t i = 0; i < kv_size(hl_msg); i++) { HlMessageChunk chunk = kv_A(hl_msg, i); msg_multiline_attr((const char *)chunk.text.data, chunk.attr, - false, &need_clear); + true, &need_clear); } if (history) { msg_ext_set_kind("echomsg"); @@ -1733,6 +1842,9 @@ static void write_msg(String message, bool to_err) ++no_wait_return; for (uint32_t i = 0; i < message.size; i++) { + if (got_int) { + break; + } if (to_err) { PUSH_CHAR(i, err_pos, err_line_buf, emsg); } else { diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index c516cedaf4..640144b234 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -390,7 +390,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E FUNC_API_SINCE(4) FUNC_API_FAST { int pflags = 0; - for (size_t i = 0 ; i < flags.size ; i++) { + for (size_t i = 0; i < flags.size; i++) { switch (flags.data[i]) { case 'm': pflags |= kExprFlagsMulti; break; @@ -478,7 +478,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E .capacity = kv_size(colors), .size = kv_size(colors), }; - for (size_t i = 0 ; i < kv_size(colors) ; i++) { + for (size_t i = 0; i < kv_size(colors); i++) { const ParserHighlightChunk chunk = kv_A(colors, i); Array chunk_arr = (Array) { .items = xmalloc(4 * sizeof(chunk_arr.items[0])), diff --git a/src/nvim/assert.h b/src/nvim/assert.h index ad92d9a2af..65519a8004 100644 --- a/src/nvim/assert.h +++ b/src/nvim/assert.h @@ -1,3 +1,5 @@ +// uncrustify:off + #ifndef NVIM_ASSERT_H #define NVIM_ASSERT_H diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c index af519dcba9..d7f73fa4a1 100644 --- a/src/nvim/aucmd.c +++ b/src/nvim/aucmd.c @@ -25,13 +25,14 @@ void do_autocmd_uienter(uint64_t chanid, bool attached) } recursive = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); assert(chanid < VARNUMBER_MAX); tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid); tv_dict_set_keys_readonly(dict); apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE, NULL, NULL, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 6be51c504c..8fe623fc96 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -37,6 +37,7 @@ return { 'CursorHoldI', -- idem, in Insert mode 'CursorMoved', -- cursor was moved 'CursorMovedI', -- cursor was moved in Insert mode + 'DiagnosticChanged', -- diagnostics in a buffer were modified 'DiffUpdated', -- diffs have been updated 'DirChanged', -- directory changed 'EncodingChanged', -- after changing the 'encoding' option @@ -69,11 +70,15 @@ return { 'InsertLeave', -- just after leaving Insert mode 'InsertLeavePre', -- just before leaving Insert mode 'MenuPopup', -- just before popup menu is displayed + 'ModeChanged', -- after changing the mode 'OptionSet', -- after setting any option 'QuickFixCmdPost', -- after :make, :grep etc. 'QuickFixCmdPre', -- before :make, :grep etc. 'QuitPre', -- before :quit + 'RecordingEnter', -- when starting to record a macro + 'RecordingLeave', -- just before a macro stops recording 'RemoteReply', -- upon string reception from a remote vim + 'SearchWrapped', -- after the search wrapped around 'SessionLoadPost', -- after loading a session file 'ShellCmdPost', -- after ":!cmd" 'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd". @@ -126,7 +131,10 @@ return { -- syntax file nvim_specific = { BufModifiedSet=true, + DiagnosticChanged=true, DirChanged=true, + RecordingEnter=true, + RecordingLeave=true, Signal=true, TabClosed=true, TabNew=true, diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2d0c0f3fd5..3780cad1d6 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -15,8 +15,8 @@ #include "nvim/ex_docmd.h" #include "nvim/fileio.h" #include "nvim/getchar.h" -#include "nvim/misc1.h" #include "nvim/option.h" +#include "nvim/os/input.h" #include "nvim/regexp.h" #include "nvim/search.h" #include "nvim/state.h" @@ -925,6 +925,13 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, c return FAIL; } } + + // need to initialize last_mode for the first ModeChanged autocmd + if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) { + xfree(last_mode); + last_mode = get_mode(); + } + ap->cmds = NULL; *prev_ap = ap; last_autopat[(int)event] = ap; @@ -1206,10 +1213,13 @@ win_found: // Hmm, original window disappeared. Just use the first one. curwin = firstwin; } + curbuf = curwin->w_buffer; + // May need to restore insert mode for a prompt buffer. + entering_window(curwin); + prevwin = win_find_by_handle(aco->save_prevwin_handle); vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab - curbuf = curwin->w_buffer; xfree(globaldir); globaldir = aco->globaldir; @@ -1440,7 +1450,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, // invalid. if (fname_io == NULL) { if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE - || event == EVENT_OPTIONSET) { + || event == EVENT_OPTIONSET || event == EVENT_MODECHANGED) { autocmd_fname = NULL; } else if (fname != NULL && !ends_excmd(*fname)) { autocmd_fname = fname; @@ -1494,11 +1504,12 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, || event == EVENT_CMDWINLEAVE || event == EVENT_CMDUNDEFINED || event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE || event == EVENT_DIRCHANGED || event == EVENT_FILETYPE - || event == EVENT_FUNCUNDEFINED || event == EVENT_OPTIONSET - || event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE - || event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING - || event == EVENT_SYNTAX || event == EVENT_SIGNAL - || event == EVENT_TABCLOSED || event == EVENT_WINCLOSED) { + || event == EVENT_FUNCUNDEFINED || event == EVENT_MODECHANGED + || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST + || event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY + || event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX + || event == EVENT_SIGNAL || event == EVENT_TABCLOSED + || event == EVENT_WINCLOSED) { fname = vim_strsave(fname); } else { fname = (char_u *)FullName_save((char *)fname, false); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index e9d89c2f91..89baea83f8 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -57,7 +57,6 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os/input.h" @@ -1305,7 +1304,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) if (buf == NULL) { // No previous buffer, Try 2'nd approach forward = true; buf = curbuf->b_next; - for (;; ) { + for (;;) { if (buf == NULL) { if (!forward) { // tried both directions break; @@ -1587,7 +1586,7 @@ void do_autochdir(void) if (starting == 0 && curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname, kCdCauseAuto) == OK) { - post_chdir(kCdScopeGlobal, false); + last_chdir_reason = "autochdir"; shorten_fnames(true); } } @@ -2163,7 +2162,7 @@ int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlis // First try finding a listed buffer. If not found and "unlisted" // is true, try finding an unlisted buffer. find_listed = true; - for (;; ) { + for (;;) { for (attempt = 0; attempt <= 3; attempt++) { // may add '^' and '$' if (toggledollar) { @@ -2719,7 +2718,7 @@ void buflist_list(exarg_T *eap) IObuff[len++] = ' '; } while (--i > 0 && len < IOSIZE - 18); if (vim_strchr(eap->arg, 't') && buf->b_last_used) { - add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used); + undo_fmt_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used); } else { vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), _("line %" PRId64), @@ -3130,7 +3129,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate) // before redrawing). // - When the screen was scrolled but there is no wait-return // prompt. - set_keep_msg((char_u *)p, 0); + set_keep_msg(p, 0); } } @@ -3429,7 +3428,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use static int *stl_separator_locations = NULL; #define TMPLEN 70 - char_u buf_tmp[TMPLEN]; + char buf_tmp[TMPLEN]; char_u win_tmp[TMPLEN]; char_u *usefmt = fmt; const int save_must_redraw = must_redraw; @@ -3511,7 +3510,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // Proceed character by character through the statusline format string // fmt_p is the current position in the input buffer - for (char_u *fmt_p = usefmt; *fmt_p; ) { + for (char_u *fmt_p = usefmt; *fmt_p;) { if (curitem == (int)stl_items_len) { size_t new_len = stl_items_len * 3 / 2; @@ -3856,7 +3855,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use bool itemisflag = false; bool fillable = true; long num = -1; - char_u *str = NULL; + char *str = NULL; switch (opt) { case STL_FILEPATH: case STL_FULLPATH: @@ -3873,9 +3872,9 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use } trans_characters(NameBuff, MAXPATHL); if (opt != STL_FILENAME) { - str = NameBuff; + str = (char *)NameBuff; } else { - str = path_tail(NameBuff); + str = (char *)path_tail(NameBuff); } break; case STL_VIM_EXPR: // '{' @@ -3912,8 +3911,8 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // { Evaluate the expression // Store the current buffer number as a string variable - vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum); - set_internal_string_var("g:actual_curbuf", buf_tmp); + vim_snprintf(buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum); + set_internal_string_var("g:actual_curbuf", (char_u *)buf_tmp); vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle); set_internal_string_var("g:actual_curwin", win_tmp); @@ -3928,7 +3927,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use } // Note: The result stored in `t` is unused. - str = eval_to_string_safe(out_p, &t, use_sandbox); + str = (char *)eval_to_string_safe(out_p, &t, use_sandbox); curwin = save_curwin; curbuf = save_curbuf; @@ -3943,8 +3942,8 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // Check if the evaluated result is a number. // If so, convert the number to an int and free the string. if (str != NULL && *str != 0) { - if (*skipdigits(str) == NUL) { - num = atoi((char *)str); + if (*skipdigits((char_u *)str) == NUL) { + num = atoi(str); XFREE_CLEAR(str); itemisflag = false; } @@ -3957,8 +3956,8 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use && strchr((const char *)str, '%') != NULL && evaldepth < MAX_STL_EVAL_DEPTH) { size_t parsed_usefmt = (size_t)(block_start - usefmt); - size_t str_length = strlen((const char *)str); - size_t fmt_length = strlen((const char *)fmt_p); + size_t str_length = STRLEN(str); + size_t fmt_length = STRLEN(fmt_p); size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3; char_u *new_fmt = (char_u *)xmalloc(new_fmt_len * sizeof(char_u)); @@ -4029,7 +4028,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // Store the position percentage in our temporary buffer. // Note: We cannot store the value in `num` because // `get_rel_pos` can return a named position. Ex: "Top" - get_rel_pos(wp, buf_tmp, TMPLEN); + get_rel_pos(wp, (char_u *)buf_tmp, TMPLEN); str = buf_tmp; break; @@ -4044,14 +4043,14 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // Note: The call will only return true if it actually // appended data to the `buf_tmp` buffer. - if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) { + if (append_arg_number(wp, (char_u *)buf_tmp, (int)sizeof(buf_tmp), false)) { str = buf_tmp; } break; case STL_KEYMAP: fillable = false; - if (get_keymap_str(wp, (char_u *)"<%s>", buf_tmp, TMPLEN)) { + if (get_keymap_str(wp, (char_u *)"<%s>", (char_u *)buf_tmp, TMPLEN)) { str = buf_tmp; } break; @@ -4090,7 +4089,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use case STL_ROFLAG_ALT: itemisflag = true; if (wp->w_buffer->b_p_ro) { - str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]")); + str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"); } break; @@ -4098,8 +4097,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use case STL_HELPFLAG_ALT: itemisflag = true; if (wp->w_buffer->b_help) { - str = (char_u *)((opt == STL_HELPFLAG_ALT) ? ",HLP" - : _("[Help]")); + str = (opt == STL_HELPFLAG_ALT) ? ",HLP" : _("[Help]"); } break; @@ -4109,7 +4107,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // (including the brackets and null terminating character) if (*wp->w_buffer->b_p_ft != NUL && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) { - vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "[%s]", + vim_snprintf(buf_tmp, sizeof(buf_tmp), "[%s]", wp->w_buffer->b_p_ft); str = buf_tmp; } @@ -4122,10 +4120,10 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // (including the comma and null terminating character) if (*wp->w_buffer->b_p_ft != NUL && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) { - vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), ",%s", + vim_snprintf(buf_tmp, sizeof(buf_tmp), ",%s", wp->w_buffer->b_p_ft); // Uppercase the file extension - for (char_u *t = buf_tmp; *t != 0; t++) { + for (char_u *t = (char_u *)buf_tmp; *t != 0; t++) { *t = (char_u)TOUPPER_LOC(*t); } str = buf_tmp; @@ -4135,16 +4133,13 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use case STL_PREVIEWFLAG_ALT: itemisflag = true; if (wp->w_p_pvw) { - str = (char_u *)((opt == STL_PREVIEWFLAG_ALT) ? ",PRV" - : _("[Preview]")); + str = (opt == STL_PREVIEWFLAG_ALT) ? ",PRV" : _("[Preview]"); } break; case STL_QUICKFIX: if (bt_quickfix(wp->w_buffer)) { - str = (char_u *)(wp->w_llist_ref - ? _(msg_loclist) - : _(msg_qflist)); + str = wp->w_llist_ref ? _(msg_loclist) : _(msg_qflist); } break; @@ -4155,17 +4150,17 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use + bufIsChanged(wp->w_buffer) * 2 + (!MODIFIABLE(wp->w_buffer)) * 4) { case 2: - str = (char_u *)"[+]"; break; + str = "[+]"; break; case 3: - str = (char_u *)",+"; break; + str = ",+"; break; case 4: - str = (char_u *)"[-]"; break; + str = "[-]"; break; case 5: - str = (char_u *)",-"; break; + str = ",-"; break; case 6: - str = (char_u *)"[+-]"; break; + str = "[+-]"; break; case 7: - str = (char_u *)",+-"; break; + str = ",+-"; break; } break; @@ -4199,7 +4194,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use if (str != NULL && *str) { // { Skip the leading `,` or ` ` if the item is a flag // and the proper conditions are met - char_u *t = str; + char_u *t = (char_u *)str; if (itemisflag) { if ((t[0] && t[1]) && ((!prevchar_isitem && *t == ',') @@ -4411,7 +4406,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // string to find the last character that will fit. trunc_p = out; width = 0; - for (;; ) { + for (;;) { width += ptr2cells(trunc_p); if (width >= maxwidth) { break; @@ -4762,7 +4757,7 @@ void do_arg_all(int count, int forceit, int keep_tabs) if (had_tab > 0) { goto_tabpage_tp(first_tabpage, true, true); } - for (;; ) { + for (;;) { win_T *wpnext = NULL; tpnext = curtab->tp_next; for (win_T *wp = firstwin; wp != NULL; wp = wpnext) { @@ -5013,7 +5008,7 @@ void ex_buffer_all(exarg_T *eap) if (had_tab > 0) { goto_tabpage_tp(first_tabpage, true, true); } - for (;; ) { + for (;;) { tpnext = curtab->tp_next; for (wp = firstwin; wp != NULL; wp = wpnext) { wpnext = wp->w_next; @@ -5024,8 +5019,8 @@ void ex_buffer_all(exarg_T *eap) : wp->w_width != Columns) || (had_tab > 0 && wp != firstwin)) && !ONE_WINDOW - && !(wp->w_closing || - wp->w_buffer->b_locked > 0)) { + && !(wp->w_closing + || wp->w_buffer->b_locked > 0)) { win_close(wp, false); wpnext = firstwin; // just in case an autocommand does // something strange with windows @@ -5142,7 +5137,7 @@ void ex_buffer_all(exarg_T *eap) /* * Close superfluous windows. */ - for (wp = lastwin; open_wins > count; ) { + for (wp = lastwin; open_wins > count;) { r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer) || autowrite(wp->w_buffer, false) == OK); if (!win_valid(wp)) { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index bd9c5efa44..49e527e98b 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1121,14 +1121,14 @@ typedef struct { /// @{ enum { MENU_INDEX_INVALID = -1, - MENU_INDEX_NORMAL = 0, - MENU_INDEX_VISUAL = 1, - MENU_INDEX_SELECT = 2, - MENU_INDEX_OP_PENDING = 3, - MENU_INDEX_INSERT = 4, - MENU_INDEX_CMDLINE = 5, - MENU_INDEX_TIP = 6, - MENU_MODES = 7, + MENU_INDEX_NORMAL = 0, + MENU_INDEX_VISUAL = 1, + MENU_INDEX_SELECT = 2, + MENU_INDEX_OP_PENDING = 3, + MENU_INDEX_INSERT = 4, + MENU_INDEX_CMDLINE = 5, + MENU_INDEX_TIP = 6, + MENU_MODES = 7, }; typedef struct VimMenu vimmenu_T; diff --git a/src/nvim/change.c b/src/nvim/change.c index 6ec4979dac..1dbbfff024 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -19,7 +19,6 @@ #include "nvim/indent_c.h" #include "nvim/mark.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/plines.h" @@ -131,7 +130,7 @@ void changed_internal(void) curbuf->b_changed = true; curbuf->b_changed_invalid = true; ml_setflags(curbuf); - check_status(curbuf); + redraw_buf_status_later(curbuf); redraw_tabline = true; need_maketitle = true; // set window title later } @@ -517,7 +516,7 @@ void unchanged(buf_T *buf, int ff, bool always_inc_changedtick) if (ff) { save_file_ff(buf); } - check_status(buf); + redraw_buf_status_later(buf); redraw_tabline = true; need_maketitle = true; // set window title later buf_inc_changedtick(buf); @@ -625,7 +624,7 @@ void ins_char_bytes(char_u *buf, size_t charlen) } } - char_u *newp = xmalloc((size_t)(linelen + newlen - oldlen)); + char_u *newp = xmalloc(linelen + newlen - oldlen); // Copy bytes before the cursor. if (col > 0) { @@ -1355,7 +1354,7 @@ int open_line(int dir, int flags, int second_line_indent) int c = 0; int off = 0; - for (p = lead_flags; *p != NUL && *p != ':'; ) { + for (p = lead_flags; *p != NUL && *p != ':';) { if (*p == COM_RIGHT || *p == COM_LEFT) { c = *p++; } else if (ascii_isdigit(*p) || *p == '-') { @@ -1841,7 +1840,7 @@ void del_lines(long nlines, bool undo) return; } - for (n = 0; n < nlines; ) { + for (n = 0; n < nlines;) { if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to delete break; } @@ -1863,3 +1862,289 @@ void del_lines(long nlines, bool undo) // adjust marks, mark the buffer as changed and prepare for displaying deleted_lines_mark(first, n); } + +/// Returns the length in bytes of the prefix of the given string which introduces a comment. +/// +/// If this string is not a comment then 0 is returned. +/// When "flags" is not NULL, it is set to point to the flags of the recognized comment leader. +/// "backward" must be true for the "O" command. +/// If "include_space" is set, include trailing whitespace while calculating the length. +int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space) +{ + int i, j; + int result; + int got_com = false; + int found_one; + char_u part_buf[COM_MAX_LEN]; // buffer for one option part + char_u *string; // pointer to comment string + char_u *list; + int middle_match_len = 0; + char_u *prev_list; + char_u *saved_flags = NULL; + + result = i = 0; + while (ascii_iswhite(line[i])) { // leading white space is ignored + i++; + } + + // Repeat to match several nested comment strings. + while (line[i] != NUL) { + // scan through the 'comments' option for a match + found_one = false; + for (list = curbuf->b_p_com; *list;) { + // Get one option part into part_buf[]. Advance "list" to next + // one. Put "string" at start of string. + if (!got_com && flags != NULL) { + *flags = list; // remember where flags started + } + prev_list = list; + (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); + string = vim_strchr(part_buf, ':'); + if (string == NULL) { // missing ':', ignore this part + continue; + } + *string++ = NUL; // isolate flags from string + + // If we found a middle match previously, use that match when this + // is not a middle or end. + if (middle_match_len != 0 + && vim_strchr(part_buf, COM_MIDDLE) == NULL + && vim_strchr(part_buf, COM_END) == NULL) { + break; + } + + // When we already found a nested comment, only accept further + // nested comments. + if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) { + continue; + } + + // When 'O' flag present and using "O" command skip this one. + if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) { + continue; + } + + // Line contents and string must match. + // When string starts with white space, must have some white space + // (but the amount does not need to match, there might be a mix of + // TABs and spaces). + if (ascii_iswhite(string[0])) { + if (i == 0 || !ascii_iswhite(line[i - 1])) { + continue; // missing white space + } + while (ascii_iswhite(string[0])) { + string++; + } + } + for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) { + } + if (string[j] != NUL) { + continue; // string doesn't match + } + // When 'b' flag used, there must be white space or an + // end-of-line after the string in the line. + if (vim_strchr(part_buf, COM_BLANK) != NULL + && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { + continue; + } + + // We have found a match, stop searching unless this is a middle + // comment. The middle comment can be a substring of the end + // comment in which case it's better to return the length of the + // end comment and its flags. Thus we keep searching with middle + // and end matches and use an end match if it matches better. + if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { + if (middle_match_len == 0) { + middle_match_len = j; + saved_flags = prev_list; + } + continue; + } + if (middle_match_len != 0 && j > middle_match_len) { + // Use this match instead of the middle match, since it's a + // longer thus better match. + middle_match_len = 0; + } + + if (middle_match_len == 0) { + i += j; + } + found_one = true; + break; + } + + if (middle_match_len != 0) { + // Use the previously found middle match after failing to find a + // match with an end. + if (!got_com && flags != NULL) { + *flags = saved_flags; + } + i += middle_match_len; + found_one = true; + } + + // No match found, stop scanning. + if (!found_one) { + break; + } + + result = i; + + // Include any trailing white space. + while (ascii_iswhite(line[i])) { + i++; + } + + if (include_space) { + result = i; + } + + // If this comment doesn't nest, stop here. + got_com = true; + if (vim_strchr(part_buf, COM_NEST) == NULL) { + break; + } + } + return result; +} + +/// Return the offset at which the last comment in line starts. If there is no +/// comment in the whole line, -1 is returned. +/// +/// When "flags" is not null, it is set to point to the flags describing the +/// recognized comment leader. +int get_last_leader_offset(char_u *line, char_u **flags) +{ + int result = -1; + int i, j; + int lower_check_bound = 0; + char_u *string; + char_u *com_leader; + char_u *com_flags; + char_u *list; + int found_one; + char_u part_buf[COM_MAX_LEN]; // buffer for one option part + + // Repeat to match several nested comment strings. + i = (int)STRLEN(line); + while (--i >= lower_check_bound) { + // scan through the 'comments' option for a match + found_one = false; + for (list = curbuf->b_p_com; *list;) { + char_u *flags_save = list; + + // Get one option part into part_buf[]. Advance list to next one. + // put string at start of string. + (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); + string = vim_strchr(part_buf, ':'); + if (string == NULL) { // If everything is fine, this cannot actually + // happen. + continue; + } + *string++ = NUL; // Isolate flags from string. + com_leader = string; + + // Line contents and string must match. + // When string starts with white space, must have some white space + // (but the amount does not need to match, there might be a mix of + // TABs and spaces). + if (ascii_iswhite(string[0])) { + if (i == 0 || !ascii_iswhite(line[i - 1])) { + continue; + } + while (ascii_iswhite(*string)) { + string++; + } + } + for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) { + // do nothing + } + if (string[j] != NUL) { + continue; + } + + // When 'b' flag used, there must be white space or an + // end-of-line after the string in the line. + if (vim_strchr(part_buf, COM_BLANK) != NULL + && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { + continue; + } + + if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { + // For a middlepart comment, only consider it to match if + // everything before the current position in the line is + // whitespace. Otherwise we would think we are inside a + // comment if the middle part appears somewhere in the middle + // of the line. E.g. for C the "*" appears often. + for (j = 0; j <= i && ascii_iswhite(line[j]); j++) { + } + if (j < i) { + continue; + } + } + + // We have found a match, stop searching. + found_one = true; + + if (flags) { + *flags = flags_save; + } + com_flags = flags_save; + + break; + } + + if (found_one) { + char_u part_buf2[COM_MAX_LEN]; // buffer for one option part + int len1, len2, off; + + result = i; + // If this comment nests, continue searching. + if (vim_strchr(part_buf, COM_NEST) != NULL) { + continue; + } + + lower_check_bound = i; + + // Let's verify whether the comment leader found is a substring + // of other comment leaders. If it is, let's adjust the + // lower_check_bound so that we make sure that we have determined + // the comment leader correctly. + + while (ascii_iswhite(*com_leader)) { + com_leader++; + } + len1 = (int)STRLEN(com_leader); + + for (list = curbuf->b_p_com; *list;) { + char_u *flags_save = list; + + (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ","); + if (flags_save == com_flags) { + continue; + } + string = vim_strchr(part_buf2, ':'); + string++; + while (ascii_iswhite(*string)) { + string++; + } + len2 = (int)STRLEN(string); + if (len2 == 0) { + continue; + } + + // Now we have to verify whether string ends with a substring + // beginning the com_leader. + for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) { + off--; + if (!STRNCMP(string + off, com_leader, len2 - off)) { + if (i - off < lower_check_bound) { + lower_check_bound = i - off; + } + } + } + } + } + } + return result; +} diff --git a/src/nvim/change.h b/src/nvim/change.h index e1a1bfba17..e7c8a2b031 100644 --- a/src/nvim/change.h +++ b/src/nvim/change.h @@ -4,6 +4,13 @@ #include "nvim/buffer_defs.h" // for buf_T #include "nvim/pos.h" // for linenr_T +// flags for open_line() +#define OPENLINE_DELSPACES 1 // delete spaces after cursor +#define OPENLINE_DO_COM 2 // format comments +#define OPENLINE_KEEPTRAIL 4 // keep trailing spaces +#define OPENLINE_MARKFIX 8 // fix mark positions +#define OPENLINE_COM_LIST 16 // format comments with list/2nd line indent + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "change.h.generated.h" #endif diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 9662f6205f..cd5134fe5f 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -821,7 +821,8 @@ static void set_info_event(void **argv) Channel *chan = argv[0]; event_T event = (event_T)(ptrdiff_t)argv[1]; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); Dictionary info = channel_info(chan->id); typval_T retval; (void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); @@ -829,7 +830,7 @@ static void set_info_event(void **argv) apply_autocmds(event, NULL, NULL, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); api_free_dictionary(info); channel_decref(chan); } diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 5e18f9d86e..599d662993 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -21,7 +21,6 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os_unix.h" @@ -1642,6 +1641,16 @@ int hex2nr(int c) return c - '0'; } +/// Convert two hex characters to a byte. +/// Return -1 if one of the characters is not hex. +int hexhex2nr(char_u *p) +{ + if (!ascii_isxdigit(p[0]) || !ascii_isxdigit(p[1])) { + return -1; + } + return (hex2nr(p[0]) << 4) + hex2nr(p[1]); +} + /// Check that "str" starts with a backslash that should be removed. /// For Windows this is only done when the character after the /// backslash is not a normal file name character. diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 47d89717e8..c4e5d9522b 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -14,8 +14,8 @@ /// @return Folded variant. #define CH_FOLD(c) \ utf_fold((sizeof(c) == sizeof(char)) \ - ?((int)(uint8_t)(c)) \ - :((int)(c))) + ? ((int)(uint8_t)(c)) \ + : ((int)(c))) /// Flags for vim_str2nr() typedef enum { diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index e334fd166e..6e2c6232d7 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -14,7 +14,6 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/plines.h" #include "nvim/screen.h" @@ -108,6 +107,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a int head = 0; one_more = (State & INSERT) + || (State & TERM_FOCUS) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL); diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 644bb2c324..6b0a5dfe12 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -168,7 +168,7 @@ char *parse_shape_opt(int what) } // Parse the part after the colon - for (p = colonp + 1; *p && *p != ','; ) { + for (p = colonp + 1; *p && *p != ',';) { { /* * First handle the ones with a number argument. diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index 3ac128a20f..b6e35f3047 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -109,7 +109,7 @@ void do_debug(char_u *cmd) smsg(_("cmd: %s"), cmd); } // Repeat getting a command and executing it. - for (;; ) { + for (;;) { msg_scroll = true; need_wait_return = false; // Save the current typeahead buffer and replace it with an empty one. diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 4e03f4761c..0233b3a5ab 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -29,7 +29,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" @@ -39,6 +38,7 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/undo.h" +#include "nvim/ui.h" #include "nvim/vim.h" #include "nvim/window.h" #include "xdiff/xdiff.h" @@ -739,7 +739,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) len = 0; for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (s = ml_get_buf(buf, lnum, false); *s != NUL; ) { + for (s = ml_get_buf(buf, lnum, false); *s != NUL;) { if (diff_flags & DIFF_ICASE) { char_u cbuf[MB_MAXBYTES + 1]; diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index d1dd9b8309..8eda173cac 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -20,7 +20,6 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/normal.h" #include "nvim/os/input.h" #include "nvim/screen.h" diff --git a/src/nvim/edit.c b/src/nvim/edit.c index ca903fdcf7..2135d0bcd2 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -35,7 +35,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -227,6 +226,7 @@ typedef struct insert_state { cmdarg_T *ca; int mincol; int cmdchar; + int cmdchar_todo; // cmdchar to handle once in init_prompt int startln; long count; int c; @@ -290,6 +290,7 @@ static void insert_enter(InsertState *s) s->did_backspace = true; s->old_topfill = -1; s->replaceState = REPLACE; + s->cmdchar_todo = s->cmdchar; // Remember whether editing was restarted after CTRL-O did_restart_edit = restart_edit; // sleep before redrawing, needed for "CTRL-O :" that results in an @@ -385,6 +386,7 @@ static void insert_enter(InsertState *s) State = INSERT; } + trigger_modechanged(); stop_insert_mode = false; // Need to recompute the cursor position, it might move when the cursor is @@ -584,7 +586,8 @@ static int insert_check(VimState *state) } if (bt_prompt(curbuf)) { - init_prompt(s->cmdchar); + init_prompt(s->cmdchar_todo); + s->cmdchar_todo = NUL; } // If we inserted a character at the last position of the last line in the @@ -654,10 +657,17 @@ static int insert_check(VimState *state) static int insert_execute(VimState *state, int key) { + InsertState *const s = (InsertState *)state; + if (stop_insert_mode) { + // Insert mode ended, possibly from a callback. + s->count = 0; + s->nomove = true; + return 0; + } + if (key == K_IGNORE || key == K_NOP) { return -1; // get another key } - InsertState *s = (InsertState *)state; s->c = key; // Don't want K_EVENT with cursorhold for the second key, e.g., after CTRL-V. @@ -821,6 +831,16 @@ static int insert_execute(VimState *state, int key) return insert_handle_key(s); } + +/// Return true when need to go to Insert mode because of 'insertmode'. +/// +/// Don't do this when still processing a command or a mapping. +/// Don't do this when inside a ":normal" command. +bool goto_im(void) +{ + return p_im && stuff_empty() && typebuf_typed(); +} + static int insert_handle_key(InsertState *s) { // The big switch to handle a character in insert mode. @@ -983,6 +1003,15 @@ static int insert_handle_key(InsertState *s) break; case Ctrl_W: // delete word before the cursor + if (bt_prompt(curbuf) && (mod_mask & MOD_MASK_SHIFT) == 0) { + // In a prompt window CTRL-W is used for window commands. + // Use Shift-CTRL-W to delete a word. + stuffcharReadbuff(Ctrl_W); + restart_edit = 'A'; + s->nomove = true; + s->count = 0; + return 0; + } s->did_backspace = ins_bs(s->c, BACKSPACE_WORD, &s->inserted_space); auto_format(false, true); break; @@ -1652,10 +1681,21 @@ static void init_prompt(int cmdchar_todo) coladvance(MAXCOL); changed_bytes(curbuf->b_ml.ml_line_count, 0); } + + // Insert always starts after the prompt, allow editing text after it. + if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) { + Insstart.lnum = curwin->w_cursor.lnum; + Insstart.col = STRLEN(prompt); + Insstart_orig = Insstart; + Insstart_textlen = Insstart.col; + Insstart_blank_vcol = MAXCOL; + arrow_used = false; + } + if (cmdchar_todo == 'A') { coladvance(MAXCOL); } - if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) { + if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) { curwin->w_cursor.col = STRLEN(prompt); } // Make sure the cursor is in a valid position. @@ -2048,6 +2088,8 @@ static void ins_ctrl_x(void) // CTRL-V look like CTRL-N ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; } + + trigger_modechanged(); } // Whether other than default completion has been selected. @@ -2660,6 +2702,7 @@ void set_completion(colnr_T startcol, list_T *list) show_pum(save_w_wrow, save_w_leftcol); } + trigger_modechanged(); ui_flush(); } @@ -2715,12 +2758,13 @@ static bool pum_enough_matches(void) static void trigger_complete_changed_event(int cur) { static bool recursive = false; + save_v_event_T save_v_event; if (recursive) { return; } - dict_T *v_event = get_vim_var_dict(VV_EVENT); + dict_T *v_event = get_v_event(&save_v_event); if (cur < 0) { tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc()); } else { @@ -2736,7 +2780,7 @@ static void trigger_complete_changed_event(int cur) textlock--; recursive = false; - tv_dict_clear(v_event); + restore_v_event(v_event, &save_v_event); } /// Show the popup menu for the list of matches. @@ -3292,16 +3336,11 @@ void get_complete_info(list_T *what_list, dict_T *retdict) dict_T *di = tv_dict_alloc(); tv_list_append_dict(li, di); - tv_dict_add_str(di, S_LEN("word"), - (char *)EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(di, S_LEN("abbr"), - (char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(di, S_LEN("menu"), - (char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(di, S_LEN("kind"), - (char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(di, S_LEN("info"), - (char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); if (match->cp_user_data.v_type == VAR_UNKNOWN) { tv_dict_add_str(di, S_LEN("user_data"), ""); } else { @@ -3843,6 +3882,8 @@ static bool ins_compl_prep(int c) ins_apply_autocmds(EVENT_COMPLETEDONE); } + trigger_modechanged(); + /* reset continue_* if we left expansion-mode, if we stay they'll be * (re)set properly in ins_complete() */ if (!vim_is_ctrl_x_key(c)) { @@ -3924,7 +3965,8 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) /// Get the user-defined completion function name for completion 'type' -static char_u *get_complete_funcname(int type) { +static char_u *get_complete_funcname(int type) +{ switch (type) { case CTRL_X_FUNCTION: return curbuf->b_p_cfu; @@ -4171,7 +4213,7 @@ static int ins_compl_get_exp(pos_T *ini) pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; // For ^N/^P loop over all the flags/windows/buffers in 'complete' - for (;; ) { + for (;;) { found_new_match = FAIL; set_match_pos = false; @@ -4389,7 +4431,7 @@ static int ins_compl_get_exp(pos_T *ini) p_ws = true; } bool looped_around = false; - for (;; ) { + for (;;) { bool cont_s_ipos = false; msg_silent++; // Don't want messages for wrapscan. @@ -4591,6 +4633,8 @@ static int ins_compl_get_exp(pos_T *ini) compl_curr_match = compl_old_match; } } + trigger_modechanged(); + return i; } @@ -4635,16 +4679,11 @@ static dict_T *ins_compl_dict_alloc(compl_T *match) { // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); - tv_dict_add_str(dict, S_LEN("word"), - (const char *)EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(dict, S_LEN("abbr"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(dict, S_LEN("menu"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(dict, S_LEN("kind"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(dict, S_LEN("info"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + tv_dict_add_str(dict, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(dict, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(dict, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(dict, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(dict, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); if (match->cp_user_data.v_type == VAR_UNKNOWN) { tv_dict_add_str(dict, S_LEN("user_data"), ""); } else { @@ -5241,7 +5280,7 @@ static int ins_complete(int c, bool enable_pum) funcname = get_complete_funcname(ctrl_x_mode); if (*funcname == NUL) { semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION - ? "completefunc" : "omnifunc"); + ? "completefunc" : "omnifunc"); // restore did_ai, so that adding comment leader works did_ai = save_did_ai; return FAIL; @@ -5580,7 +5619,7 @@ int get_literal(void) no_mapping++; // don't map the next key hits cc = 0; i = 0; - for (;; ) { + for (;;) { nc = plain_vgetc(); if (!(State & CMDLINE) && MB_BYTE2LEN_CHECK(nc) == 1) { @@ -6574,7 +6613,7 @@ static void spell_back_to_badword(void) int stop_arrow(void) { if (arrow_used) { - Insstart = curwin->w_cursor; //new insertion starts here + Insstart = curwin->w_cursor; // new insertion starts here if (Insstart.col > Insstart_orig.col && !ins_need_undo) { // Don't update the original insert position when moved to the // right, except when nothing was inserted yet. @@ -6687,7 +6726,7 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) curwin->w_cursor = *end_insert_pos; check_cursor_col(); // make sure it is not past the line - for (;; ) { + for (;;) { if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) { --curwin->w_cursor.col; } @@ -6878,7 +6917,7 @@ int oneleft(void) // We might get stuck on 'showbreak', skip over it. width = 1; - for (;; ) { + for (;;) { coladvance(v - width); // getviscol() is slow, skip it when 'showbreak' is empty, // 'breakindent' is not set and there are no multi-byte @@ -7212,7 +7251,7 @@ static void replace_join(int off) { int i; - for (i = replace_stack_nr; --i >= 0; ) { + for (i = replace_stack_nr; --i >= 0;) { if (replace_stack[i] == NUL && off-- <= 0) { --replace_stack_nr; memmove(replace_stack + i, replace_stack + i + 1, @@ -7261,7 +7300,7 @@ static void mb_replace_pop_ins(int cc) } // Handle composing chars. - for (;; ) { + for (;;) { c = replace_pop(); if (c == -1) { // stack empty break; @@ -7640,16 +7679,34 @@ int hkmap(int c) KAFsofit, hKAF, LAMED, MEMsofit, MEM, NUNsofit, NUN, SAMEH, AIN, PEIsofit, PEI, ZADIsofit, ZADI, KOF, RESH, hSHIN, TAV, }; - static char_u map[26] = - { (char_u)hALEF /*a*/, (char_u)BET /*b*/, (char_u)hKAF /*c*/, - (char_u)DALET /*d*/, (char_u)-1 /*e*/, (char_u)PEIsofit /*f*/, - (char_u)GIMEL /*g*/, (char_u)HEI /*h*/, (char_u)IUD /*i*/, - (char_u)HET /*j*/, (char_u)KOF /*k*/, (char_u)LAMED /*l*/, - (char_u)MEM /*m*/, (char_u)NUN /*n*/, (char_u)SAMEH /*o*/, - (char_u)PEI /*p*/, (char_u)-1 /*q*/, (char_u)RESH /*r*/, - (char_u)ZAIN /*s*/, (char_u)TAV /*t*/, (char_u)TET /*u*/, - (char_u)VAV /*v*/, (char_u)hSHIN /*w*/, (char_u)-1 /*x*/, - (char_u)AIN /*y*/, (char_u)ZADI /*z*/ }; + static char_u map[26] = { + (char_u)hALEF, // a + (char_u)BET, // b + (char_u)hKAF, // c + (char_u)DALET, // d + (char_u)-1, // e + (char_u)PEIsofit, // f + (char_u)GIMEL, // g + (char_u)HEI, // h + (char_u)IUD, // i + (char_u)HET, // j + (char_u)KOF, // k + (char_u)LAMED, // l + (char_u)MEM, // m + (char_u)NUN, // n + (char_u)SAMEH, // o + (char_u)PEI, // p + (char_u)-1, // q + (char_u)RESH, // r + (char_u)ZAIN, // s + (char_u)TAV, // t + (char_u)TET, // u + (char_u)VAV, // v + (char_u)hSHIN, // w + (char_u)-1, // x + (char_u)AIN, // y + (char_u)ZADI, // z + }; if (c == 'N' || c == 'M' || c == 'P' || c == 'C' || c == 'Z') { return (int)(map[CharOrd(c)] - 1 + p_aleph); @@ -7975,6 +8032,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) State = NORMAL; + trigger_modechanged(); // need to position cursor again (e.g. when on a TAB ) changed_cline_bef_curs(); @@ -8076,6 +8134,7 @@ static void ins_insert(int replaceState) } else { State = replaceState | (State & LANGMAP); } + trigger_modechanged(); AppendCharToRedobuff(K_INS); showmode(); ui_cursor_shape(); // may show different cursor shape @@ -8221,7 +8280,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) || (!revins_on && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) || (!can_bs(BS_START) - && (arrow_used + && ((arrow_used && !bt_prompt(curbuf)) || (curwin->w_cursor.lnum == Insstart_orig.lnum && curwin->w_cursor.col <= Insstart_orig.col))) || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 @@ -9022,7 +9081,7 @@ static bool ins_tab(void) // correct replace stack. if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { - for (temp = i; --temp >= 0; ) { + for (temp = i; --temp >= 0;) { replace_join(repl_off); } } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0123c7265b..86384bc5b2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -35,7 +35,6 @@ #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" @@ -134,13 +133,15 @@ typedef struct { .vv_flags = flags, \ } +#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables + // Array to hold the value of v: variables. // The value is in a dictitem, so that it can also be used in the v: scope. // The reason to use this table anyway is for very quick access to the // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { @@ -150,100 +151,103 @@ static struct vimvar { // VV_SEND_SERVER "servername" // VV_REG "register" // VV_OP "operator" - VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), - VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), - VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), - VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), - VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), - VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), - VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), - VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), - VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), - VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), - VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), - VV(VV_FNAME, "fname", VAR_STRING, VV_RO), - VV(VV_LANG, "lang", VAR_STRING, VV_RO), - VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), - VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), - VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), - VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), - VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), - VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), - VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), - VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), - VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), - VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), - VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), - VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), - VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), - VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), - VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), - VV(VV_REG, "register", VAR_STRING, VV_RO), - VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), - VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), - VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), - VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), - VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), - VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), - VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), - VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), - VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), - VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), - VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), - VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), - VV(VV_CHAR, "char", VAR_STRING, 0), - VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), - VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), - VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), - VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), - VV(VV_OP, "operator", VAR_STRING, VV_RO), - VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), - VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), - VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), - VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), - VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), - VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), - VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), - VV(VV_ERRORS, "errors", VAR_LIST, 0), - VV(VV_FALSE, "false", VAR_BOOL, VV_RO), - VV(VV_TRUE, "true", VAR_BOOL, VV_RO), - VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), - VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), - VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), - VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), - VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), - VV(VV_TESTING, "testing", VAR_NUMBER, 0), - VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), - VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), - VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), - VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), - VV(VV_EVENT, "event", VAR_DICT, VV_RO), - VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), - VV(VV_ARGV, "argv", VAR_LIST, VV_RO), - VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), - VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO), + VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_FALSE, "false", VAR_BOOL, VV_RO), + VV(VV_TRUE, "true", VAR_BOOL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), + VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), + VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), + VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), + VV(VV_TESTING, "testing", VAR_NUMBER, 0), + VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), + VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), + VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), + VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), + VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), + VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), // Neovim - VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), - VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), - VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), - VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), - VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), - VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), - VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), + VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), }; #undef VV @@ -298,6 +302,31 @@ const list_T *eval_msgpack_type_lists[] = { [kMPExt] = NULL, }; +dict_T *get_v_event(save_v_event_T *sve) +{ + dict_T *v_event = get_vim_var_dict(VV_EVENT); + + if (v_event->dv_hashtab.ht_used > 0) { + // recursive use of v:event, save, make empty and restore later + sve->sve_did_save = true; + sve->sve_hashtab = v_event->dv_hashtab; + hash_init(&v_event->dv_hashtab); + } else { + sve->sve_did_save = false; + } + return v_event; +} + +void restore_v_event(dict_T *v_event, save_v_event_T *sve) +{ + tv_dict_free_contents(v_event); + if (sve->sve_did_save) { + v_event->dv_hashtab = sve->sve_hashtab; + } else { + hash_init(&v_event->dv_hashtab); + } +} + // Return "n1" divided by "n2", taking care of dividing by zero. varnumber_T num_divide(varnumber_T n1, varnumber_T n2) FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT @@ -344,7 +373,7 @@ void eval_init(void) for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; - assert(STRLEN(p->vv_name) <= 16); + assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) { p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; @@ -1551,7 +1580,7 @@ static const char_u *skip_var_list(const char_u *arg, int *var_count, int *semic if (*arg == '[') { // "[var, var]": find the matching ']'. p = arg; - for (;; ) { + for (;;) { p = skipwhite(p + 1); // skip whites after '[', ';' or ',' s = skip_var_one(p); if (s == p) { @@ -2448,7 +2477,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co // Check whether any of the list items is locked for (ri = tv_list_first(rettv->vval.v_list); - ri != NULL && ll_li != NULL; ) { + ri != NULL && ll_li != NULL;) { if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, TV_CSTRING)) { return; @@ -2464,7 +2493,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co /* * Assign the List values to the list items. */ - for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) { + for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) { if (op != NULL && *op != '=') { eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); } else { @@ -2691,7 +2720,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) xp->xp_context = EXPAND_USER_VARS; if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL) { // ":let var1 var2 ...": find last space. - for (p = arg + STRLEN(arg); p >= arg; ) { + for (p = arg + STRLEN(arg); p >= arg;) { xp->xp_pattern = p; MB_PTR_BACK(arg, p); if (ascii_iswhite(*p)) { @@ -3732,7 +3761,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) /* * Repeat computing, until no '+', '-' or '.' is following. */ - for (;; ) { + for (;;) { op = **arg; if (op != '+' && op != '-' && op != '.') { break; @@ -3907,7 +3936,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) /* * Repeat computing, until no '*', '/' or '%' is following. */ - for (;; ) { + for (;;) { op = **arg; if (op != '*' && op != '/' && op != '%') { break; @@ -4523,7 +4552,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) * dict.name */ key = *arg + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) { + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { } if (len == 0) { return FAIL; @@ -4828,7 +4857,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval } else if (opt_type == -1) { // hidden number option rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; - } else if (opt_type == 1) { // number option + } else if (opt_type == 1 || opt_type == 2) { // number or boolean option rettv->v_type = VAR_NUMBER; rettv->vval.v_number = numval; } else { // string option @@ -4890,7 +4919,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) rettv->v_type = VAR_STRING; rettv->vval.v_string = name; - for (p = *arg + 1; *p != NUL && *p != '"'; ) { + for (p = *arg + 1; *p != NUL && *p != '"';) { if (*p == '\\') { switch (*++p) { case 'b': @@ -5026,7 +5055,7 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) rettv->v_type = VAR_STRING; rettv->vval.v_string = str; - for (p = *arg + 1; *p != NUL; ) { + for (p = *arg + 1; *p != NUL;) { if (*p == '\'') { if (p[1] != '\'') { break; @@ -6086,7 +6115,7 @@ int assert_equalfile(typval_T *argvars) snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2); } else { int64_t linecount = 1; - for (int64_t count = 0; ; count++) { + for (int64_t count = 0;; count++) { const int c1 = fgetc(fd1); const int c2 = fgetc(fd2); if (c1 == EOF) { @@ -6603,8 +6632,9 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL - && (is_funcref ? find_func(trans_name) == NULL - : !translated_function_exists((const char *)trans_name))) { + && (is_funcref + ? find_func(trans_name) == NULL + : !translated_function_exists((const char *)trans_name))) { semsg(_("E700: Unknown function: %s"), s); } else { int dict_idx = 0; @@ -6851,7 +6881,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); - + tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp)); tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer)); tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); tv_dict_add_nr(dict, S_LEN("loclist"), @@ -7146,30 +7176,30 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha typval_T tv_item = { .v_lock = VAR_UNLOCKED }; switch (what) { - case kDictListKeys: - tv_item.v_type = VAR_STRING; - tv_item.vval.v_string = vim_strsave(di->di_key); - break; - case kDictListValues: - tv_copy(&di->di_tv, &tv_item); - break; - case kDictListItems: { - // items() - list_T *const sub_l = tv_list_alloc(2); - tv_item.v_type = VAR_LIST; - tv_item.vval.v_list = sub_l; - tv_list_ref(sub_l); - - tv_list_append_owned_tv(sub_l, (typval_T) { + case kDictListKeys: + tv_item.v_type = VAR_STRING; + tv_item.vval.v_string = vim_strsave(di->di_key); + break; + case kDictListValues: + tv_copy(&di->di_tv, &tv_item); + break; + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(2); + tv_item.v_type = VAR_LIST; + tv_item.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, .vval.v_string = (char_u *)xstrdup((const char *)di->di_key), }); - tv_list_append_tv(sub_l, &di->di_tv); + tv_list_append_tv(sub_l, &di->di_tv); - break; - } + break; + } } tv_list_append_owned_tv(rettv->vval.v_list, tv_item); @@ -7382,7 +7412,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T } // Default result is zero == OK. - for (;; ) { + for (;;) { if (lines->v_type == VAR_LIST) { // List argument, get next string. if (li == NULL) { @@ -9649,8 +9679,8 @@ bool var_check_func_name(const char *const name, const bool new_var) { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] - : name[0])) { + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) { semsg(_("E704: Funcref variable name must start with a capital: %s"), name); return false; } @@ -10446,11 +10476,15 @@ void option_last_set_msg(LastSet last_set) } } -// reset v:option_new, v:option_old and v:option_type +// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, +// v:option_type, and v:option_command. void reset_v_option_vars(void) { - set_vim_var_string(VV_OPTION_NEW, NULL, -1); - set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1); + set_vim_var_string(VV_OPTION_COMMAND, NULL, -1); set_vim_var_string(VV_OPTION_TYPE, NULL, -1); } diff --git a/src/nvim/eval.h b/src/nvim/eval.h index d34348a274..a9ec5d47a6 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -138,6 +138,9 @@ typedef enum { VV_COMPLETED_ITEM, VV_OPTION_NEW, VV_OPTION_OLD, + VV_OPTION_OLDLOCAL, + VV_OPTION_OLDGLOBAL, + VV_OPTION_COMMAND, VV_OPTION_TYPE, VV_ERRORS, VV_FALSE, @@ -190,6 +193,13 @@ extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1]; #undef LAST_MSGPACK_TYPE +// Struct passed to get_v_event() and restore_v_event(). +typedef struct { + bool sve_did_save; + hashtab_T sve_hashtab; +} save_v_event_T; + + /// trans_function_name() flags typedef enum { TFN_INT = 1, ///< May use internal function name @@ -209,8 +219,8 @@ typedef enum { /// flags for find_name_end() #define FNE_INCL_BR 1 // find_name_end(): include [] in name -#define FNE_CHECK_START 2 /* find_name_end(): check name starts with - valid character */ +#define FNE_CHECK_START 2 // find_name_end(): check name starts with + // valid character typedef struct { TimeWatcher tw; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index dfc51d80af..9507a12a02 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -17,7 +17,7 @@ end -- Usable with the base key: use the last function argument as the method base. -- Value is from funcs.h file. "BASE_" prefix is omitted. -local LAST = "BASE_LAST" +-- local LAST = "BASE_LAST" (currently unused after port of v8.2.1168) return { funcs={ @@ -26,14 +26,14 @@ return { add={args=2, base=1}, ['and']={args=2, base=1}, api_info={}, - append={args=2, base=LAST}, - appendbufline={args=3, base=LAST}, + append={args=2, base=2}, + appendbufline={args=3, base=3}, argc={args={0, 1}}, argidx={}, arglistid={args={0, 2}}, argv={args={0, 2}}, asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc - assert_beeps={args={1}, base=1}, + assert_beeps={args=1, base=1}, assert_equal={args={2, 3}, base=2}, assert_equalfile={args={2, 3}, base=1}, assert_exception={args={1, 2}}, @@ -41,7 +41,7 @@ return { assert_false={args={1, 2}, base=1}, assert_inrange={args={3, 4}, base=3}, assert_match={args={2, 3}, base=2}, - assert_nobeep={args={1}}, + assert_nobeep={args=1, base=1}, assert_notequal={args={2, 3}, base=2}, assert_notmatch={args={2, 3}, base=2}, assert_report={args=1, base=1}, @@ -53,8 +53,8 @@ return { bufadd={args=1, base=1}, bufexists={args=1, base=1}, buffer_exists={args=1, base=1, func='f_bufexists'}, -- obsolete - buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete - buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete + buffer_name={args={0, 1}, base=1, func='f_bufname'}, -- obsolete + buffer_number={args={0, 1}, base=1, func='f_bufnr'}, -- obsolete buflisted={args=1, base=1}, bufload={args=1, base=1}, bufloaded={args=1, base=1}, @@ -71,7 +71,7 @@ return { chanclose={args={1, 2}}, chansend={args=2}, char2nr={args={1, 2}, base=1}, - charidx={args={2, 3}}, + charidx={args={2, 3}, base=1}, chdir={args=1, base=1}, cindent={args=1, base=1}, clearmatches={args={0, 1}, base=1}, @@ -121,7 +121,7 @@ return { filter={args=2, base=1}, finddir={args={1, 3}, base=1}, findfile={args={1, 3}, base=1}, - flatten={args={1, 2}}, + flatten={args={1, 2}, base=1}, float2nr={args=1, base=1}, floor={args=1, base=1, func="float_op_wrapper", data="&floor"}, fmod={args=2, base=1}, @@ -137,7 +137,7 @@ return { ['function']={args={1, 3}, base=1}, garbagecollect={args={0, 1}}, get={args={2, 3}, base=1}, - getbufinfo={args={0, 1}}, + getbufinfo={args={0, 1}, base=1}, getbufline={args={2, 3}, base=1}, getbufvar={args={2, 3}, base=1}, getchangelist={args={0, 1}, base=1}, @@ -152,7 +152,7 @@ return { getcompletion={args={2, 3}, base=1}, getcurpos={}, getcwd={args={0, 2}, base=1}, - getenv={args={1}, base=1}, + getenv={args=1, base=1}, getfontname={args={0, 1}}, getfperm={args=1, base=1}, getfsize={args=1, base=1}, @@ -161,7 +161,7 @@ return { getjumplist={args={0, 2}, base=1}, getline={args={1, 2}, base=1}, getloclist={args={1, 2}}, - getmarklist={args={0, 1}}, + getmarklist={args={0, 1}, base=1}, getmatches={args={0, 1}}, getmousepos={}, getpid={}, @@ -262,7 +262,7 @@ return { pow={args=2, base=1}, prevnonblank={args=1, base=1}, printf={args=varargs(1), base=2}, - prompt_getprompt={args=1}, + prompt_getprompt={args=1, base=1}, prompt_setcallback={args={2, 2}, base=1}, prompt_setinterrupt={args={2, 2}, base=1}, prompt_setprompt={args={2, 2}, base=1}, @@ -277,6 +277,7 @@ return { readfile={args={1, 3}, base=1}, reg_executing={}, reg_recording={}, + reg_recorded={}, reltime={args={0, 2}, base=1}, reltimefloat={args=1, base=1}, reltimestr={args=1, base=1}, @@ -291,82 +292,82 @@ return { rpcstart={args={1, 2}}, rpcstop={args=1}, rubyeval={args=1, base=1}, - screenattr={args=2}, - screenchar={args=2}, - screenchars={args=2}, + screenattr={args=2, base=1}, + screenchar={args=2, base=1}, + screenchars={args=2, base=1}, screencol={}, - screenpos={args=3}, + screenpos={args=3, base=1}, screenrow={}, - screenstring={args=2}, - search={args={1, 4}}, - searchcount={args={0,1}}, - searchdecl={args={1, 3}}, + screenstring={args=2, base=1}, + search={args={1, 4}, base=1}, + searchcount={args={0, 1}, base=1}, + searchdecl={args={1, 3}, base=1}, searchpair={args={3, 7}}, searchpairpos={args={3, 7}}, - searchpos={args={1, 4}}, + searchpos={args={1, 4}, base=1}, serverlist={}, serverstart={args={0, 1}}, serverstop={args=1}, - setbufline={args=3}, - setbufvar={args=3}, - setcharsearch={args=1}, - setcmdpos={args=1}, - setenv={args=2}, + setbufline={args=3, base=3}, + setbufvar={args=3, base=3}, + setcharsearch={args=1, base=1}, + setcmdpos={args=1, base=1}, + setenv={args=2, base=2}, setfperm={args=2, base=1}, - setline={args=2}, - setloclist={args={2, 4}}, - setmatches={args={1, 2}}, - setpos={args=2}, - setqflist={args={1, 3}}, - setreg={args={2, 3}}, - settabvar={args=3}, - settabwinvar={args=4}, - settagstack={args={2, 3}}, - setwinvar={args=3}, - sha256={args=1}, - shellescape={args={1, 2}}, - shiftwidth={args={0, 1}}, - sign_define={args={1, 2}}, - sign_getdefined={args={0, 1}}, - sign_getplaced={args={0, 2}}, - sign_jump={args={3, 3}}, - sign_place={args={4, 5}}, - sign_placelist={args={1}}, - sign_undefine={args={0, 1}}, - sign_unplace={args={1, 2}}, - sign_unplacelist={args={1}}, - simplify={args=1}, + setline={args=2, base=2}, + setloclist={args={2, 4}, base=2}, + setmatches={args={1, 2}, base=1}, + setpos={args=2, base=2}, + setqflist={args={1, 3}, base=1}, + setreg={args={2, 3}, base=2}, + settabvar={args=3, base=3}, + settabwinvar={args=4, base=4}, + settagstack={args={2, 3}, base=2}, + setwinvar={args=3, base=3}, + sha256={args=1, base=1}, + shellescape={args={1, 2}, base=1}, + shiftwidth={args={0, 1}, base=1}, + sign_define={args={1, 2}, base=1}, + sign_getdefined={args={0, 1}, base=1}, + sign_getplaced={args={0, 2}, base=1}, + sign_jump={args=3, base=1}, + sign_place={args={4, 5}, base=1}, + sign_placelist={args=1, base=1}, + sign_undefine={args={0, 1}, base=1}, + sign_unplace={args={1, 2}, base=1}, + sign_unplacelist={args=1, base=1}, + simplify={args=1, base=1}, sin={args=1, base=1, func="float_op_wrapper", data="&sin"}, sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"}, sockconnect={args={2,3}}, sort={args={1, 3}, base=1}, - soundfold={args=1}, + soundfold={args=1, base=1}, stdioopen={args=1}, - spellbadword={args={0, 1}}, - spellsuggest={args={1, 3}}, + spellbadword={args={0, 1}, base=1}, + spellsuggest={args={1, 3}, base=1}, split={args={1, 3}, base=1}, sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"}, stdpath={args=1}, str2float={args=1, base=1}, str2list={args={1, 2}, base=1}, - str2nr={args={1, 3}}, - strcharpart={args={2, 3}}, - strchars={args={1,2}}, - strdisplaywidth={args={1, 2}}, - strftime={args={1, 2}}, - strgetchar={args={2, 2}}, - stridx={args={2, 3}}, + str2nr={args={1, 3}, base=1}, + strcharpart={args={2, 3}, base=1}, + strchars={args={1, 2}, base=1}, + strdisplaywidth={args={1, 2}, base=1}, + strftime={args={1, 2}, base=1}, + strgetchar={args=2, base=1}, + stridx={args={2, 3}, base=1}, string={args=1, base=1}, strlen={args=1, base=1}, - strpart={args={2, 4}}, - strptime={args=2}, - strridx={args={2, 3}}, + strpart={args={2, 4}, base=1}, + strptime={args=2, base=1}, + strridx={args={2, 3}, base=1}, strtrans={args=1, base=1}, strwidth={args=1, base=1}, - submatch={args={1, 2}}, + submatch={args={1, 2}, base=1}, substitute={args=4, base=1}, - swapinfo={args={1}}, - swapname={args={1}}, + swapinfo={args=1, base=1}, + swapname={args=1, base=1}, synID={args=3}, synIDattr={args={2, 3}, base=1}, synIDtrans={args=1, base=1}, @@ -374,58 +375,58 @@ return { synstack={args=2}, system={args={1, 2}, base=1}, systemlist={args={1, 3}, base=1}, - tabpagebuflist={args={0, 1}}, + tabpagebuflist={args={0, 1}, base=1}, tabpagenr={args={0, 1}}, - tabpagewinnr={args={1, 2}}, + tabpagewinnr={args={1, 2}, base=1}, tagfiles={}, - taglist={args={1, 2}}, + taglist={args={1, 2}, base=1}, tan={args=1, base=1, func="float_op_wrapper", data="&tan"}, tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"}, tempname={}, termopen={args={1, 2}}, test_garbagecollect_now={}, test_write_list_log={args=1}, - timer_info={args={0,1}}, - timer_pause={args=2}, - timer_start={args={2,3}}, - timer_stop={args=1}, + timer_info={args={0, 1}, base=1}, + timer_pause={args=2, base=1}, + timer_start={args={2, 3}, base=1}, + timer_stop={args=1, base=1}, timer_stopall={args=0}, - tolower={args=1}, - toupper={args=1}, - tr={args=3}, - trim={args={1,3}}, + tolower={args=1, base=1}, + toupper={args=1, base=1}, + tr={args=3, base=1}, + trim={args={1, 3}, base=1}, trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"}, type={args=1, base=1}, - undofile={args=1}, + undofile={args=1, base=1}, undotree={}, uniq={args={1, 3}, base=1}, values={args=1, base=1}, - virtcol={args=1}, + virtcol={args=1, base=1}, visualmode={args={0, 1}}, wait={args={2,3}}, wildmenumode={}, - win_execute={args={2, 3}}, - win_findbuf={args=1}, - win_getid={args={0,2}}, - win_gettype={args={0,1}}, - win_gotoid={args=1}, - win_id2tabwin={args=1}, - win_id2win={args=1}, - win_screenpos={args=1}, - win_splitmove={args={2, 3}}, + win_execute={args={2, 3}, base=2}, + win_findbuf={args=1, base=1}, + win_getid={args={0, 2}, base=1}, + win_gettype={args={0, 1}, base=1}, + win_gotoid={args=1, base=1}, + win_id2tabwin={args=1, base=1}, + win_id2win={args=1, base=1}, + win_screenpos={args=1, base=1}, + win_splitmove={args={2, 3}, base=1}, winbufnr={args=1, base=1}, wincol={}, windowsversion={}, - winheight={args=1}, - winlayout={args={0, 1}}, + winheight={args=1, base=1}, + winlayout={args={0, 1}, base=1}, winline={}, - winnr={args={0, 1}}, + winnr={args={0, 1}, base=1}, winrestcmd={}, - winrestview={args=1}, + winrestview={args=1, base=1}, winsaveview={}, - winwidth={args=1}, + winwidth={args=1, base=1}, wordcount={}, - writefile={args={2, 3}}, + writefile={args={2, 3}, base=1}, xor={args=2, base=1}, }, } diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 5008945f09..797420c150 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -726,11 +726,11 @@ json_decode_string_cycle_start: semsg(_("E474: Using comma in place of colon: %.*s"), LENP(p, e)); goto json_decode_string_fail; } else if (last_container.special_val == NULL - ? (last_container.container.v_type == VAR_DICT - ? (DICT_LEN(last_container.container.vval.v_dict) == 0) - : (tv_list_len(last_container.container.vval.v_list) - == 0)) - : (tv_list_len(last_container.special_val) == 0)) { + ? (last_container.container.v_type == VAR_DICT + ? (DICT_LEN(last_container.container.vval.v_dict) == 0) + : (tv_list_len(last_container.container.vval.v_list) + == 0)) + : (tv_list_len(last_container.special_val) == 0)) { semsg(_("E474: Leading comma: %.*s"), LENP(p, e)); goto json_decode_string_fail; } diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index b457353838..6f4357421b 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -29,8 +29,6 @@ #include "nvim/message.h" #include "nvim/vim.h" // For _() -#define utf_ptr2char(b) utf_ptr2char((char_u *)b) -#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) #define utf_char2len(b) ((size_t)utf_char2len(b)) const char *const encode_bool_var_names[] = { @@ -354,20 +352,20 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s const float_T flt_ = (flt); \ switch (xfpclassify(flt_)) { \ case FP_NAN: { \ - ga_concat(gap, "str2float('nan')"); \ - break; \ + ga_concat(gap, "str2float('nan')"); \ + break; \ } \ case FP_INFINITE: { \ - if (flt_ < 0) { \ - ga_append(gap, '-'); \ - } \ - ga_concat(gap, "str2float('inf')"); \ - break; \ + if (flt_ < 0) { \ + ga_append(gap, '-'); \ + } \ + ga_concat(gap, "str2float('inf')"); \ + break; \ } \ default: { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ - ga_concat(gap, numbuf); \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, numbuf); \ } \ } \ } while (0) @@ -558,18 +556,18 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s const float_T flt_ = (flt); \ switch (xfpclassify(flt_)) { \ case FP_NAN: { \ - emsg(_("E474: Unable to represent NaN value in JSON")); \ - return FAIL; \ + emsg(_("E474: Unable to represent NaN value in JSON")); \ + return FAIL; \ } \ case FP_INFINITE: { \ - emsg(_("E474: Unable to represent infinity in JSON")); \ - return FAIL; \ + emsg(_("E474: Unable to represent infinity in JSON")); \ + return FAIL; \ } \ default: { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ - ga_concat(gap, numbuf); \ - break; \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, numbuf); \ + break; \ } \ } \ } while (0) @@ -614,8 +612,8 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const #define ENCODE_RAW(ch) \ (ch >= 0x20 && utf_printable(ch)) for (size_t i = 0; i < utf_len;) { - const int ch = utf_ptr2char(utf_buf + i); - const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i)); + const int ch = utf_ptr2char((char_u *)utf_buf + i); + const size_t shift = (ch == 0 ? 1 : ((size_t)utf_ptr2len((char_u *)utf_buf + i))); assert(shift > 0); i += shift; switch (ch) { @@ -654,11 +652,11 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const ga_append(gap, '"'); ga_grow(gap, (int)str_len); for (size_t i = 0; i < utf_len;) { - const int ch = utf_ptr2char(utf_buf + i); + const int ch = utf_ptr2char((char_u *)utf_buf + i); const size_t shift = (ch == 0? 1: utf_char2len(ch)); assert(shift > 0); // Is false on invalid unicode, but this should already be handled. - assert(ch == 0 || shift == utf_ptr2len(utf_buf + i)); + assert(ch == 0 || shift == ((size_t)utf_ptr2len((char_u *)utf_buf + i))); switch (ch) { case BS: case TAB: diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 1d9ebdb596..d43eeb4a15 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -33,12 +33,12 @@ #include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/input.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mark.h" #include "nvim/math.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" @@ -2663,12 +2663,12 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) char buf[NUMBUFLEN]; const char *fname = tv_get_string_chk(&argvars[0]); const char *const mods = tv_get_string_buf_chk(&argvars[1], buf); - if (fname == NULL) { + if (mods == NULL || fname == NULL) { fname = NULL; - } else if (mods != NULL && *mods != NUL) { + } else { len = strlen(fname); - size_t usedlen = 0; if (*mods != NUL) { + size_t usedlen = 0; (void)modify_fname((char_u *)mods, false, &usedlen, (char_u **)&fname, &fbuf, &len); } @@ -3174,7 +3174,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) bool error = false; no_mapping++; - for (;; ) { + for (;;) { // Position the cursor. Needed after a message that ends in a space, // or if event processing caused a redraw. ui_cursor_goto(msg_row, msg_col); @@ -3386,15 +3386,17 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } + const char *pattern = tv_get_string(&argvars[0]); if (strcmp(type, "cmdline") == 0) { - set_one_cmd_context(&xpc, tv_get_string(&argvars[0])); + set_one_cmd_context(&xpc, pattern); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); + xpc.xp_col = STRLEN(pattern); goto theend; } ExpandInit(&xpc); - xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]); + xpc.xp_pattern = (char_u *)pattern; xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); xpc.xp_context = cmdcomplete_str_to_type(type); if (xpc.xp_context == EXPAND_NOTHING) { @@ -3447,7 +3449,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Numbers of the scope objects (window, tab) we want the working directory // of. A `-1` means to skip this scope, a `0` means the current object. int scope_number[] = { - [kCdScopeWindow ] = 0, // Number of window to look at. + [kCdScopeWindow] = 0, // Number of window to look at. [kCdScopeTabpage] = 0, // Number of tab to look at. }; @@ -3485,11 +3487,6 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - // If the user didn't specify anything, default to window scope - if (scope == kCdScopeInvalid) { - scope = MIN_CD_SCOPE; - } - // Find the tabpage by number if (scope_number[kCdScopeTabpage] > 0) { tp = find_tabpage(scope_number[kCdScopeTabpage]); @@ -3535,16 +3532,17 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) case kCdScopeGlobal: if (globaldir) { // `globaldir` is not always set. from = globaldir; - } else if (os_dirname(cwd, MAXPATHL) == FAIL) { // Get the OS CWD. + break; + } + FALLTHROUGH; // In global directory, just need to get OS CWD. + case kCdScopeInvalid: // If called without any arguments, get OS CWD. + if (os_dirname(cwd, MAXPATHL) == FAIL) { from = (char_u *)""; // Return empty string on failure. } - break; - case kCdScopeInvalid: // We should never get here - abort(); } if (from) { - xstrlcpy((char *)cwd, (char *)from, MAXPATHL); + STRLCPY(cwd, from, MAXPATHL); } rettv->vval.v_string = vim_strsave(cwd); @@ -4667,7 +4665,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Numbers of the scope objects (window, tab) we want the working directory // of. A `-1` means to skip this scope, a `0` means the current object. int scope_number[] = { - [kCdScopeWindow ] = 0, // Number of window to look at. + [kCdScopeWindow] = 0, // Number of window to look at. [kCdScopeTabpage] = 0, // Number of tab to look at. }; @@ -4867,7 +4865,7 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = syn_name2id((const char_u *)tv_get_string(&argvars[0])); + rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0])); } /* @@ -4875,7 +4873,7 @@ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = highlight_exists((const char_u *)tv_get_string(&argvars[0])); + rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0])); } /* @@ -6182,7 +6180,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; - for (;; ) { + for (;;) { if (l != NULL) { if (li == NULL) { match = false; @@ -7400,6 +7398,11 @@ static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr) return_register(reg_recording, rettv); } +static void f_reg_recorded(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + return_register(reg_recorded, rettv); +} + /// list2proftime - convert a List to proftime_T /// /// @param arg The input list, must be of type VAR_LIST and have @@ -7727,8 +7730,8 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *const buf = xmallocz(MAXPATHL); char *cpy; - for (;; ) { - for (;; ) { + for (;;) { + for (;;) { len = readlink(p, buf, MAXPATHL); if (len <= 0) { break; @@ -8603,7 +8606,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir clearpos(&firstpos); clearpos(&foundpos); pat = pat3; - for (;; ) { + for (;;) { searchit_arg_T sia; memset(&sia, 0, sizeof(sia)); sia.sa_stop_lnum = lnum_stop; @@ -11553,6 +11556,9 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict; rettv->vval.v_number = -1; + if (check_secure()) { + return; + } if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_DICT diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 3e37e8cbb6..11bbaaed9c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -28,11 +28,10 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/os/input.h" #include "nvim/pos.h" #include "nvim/types.h" #include "nvim/vim.h" -// TODO(ZyX-I): Move line_breakcheck out of misc1 -#include "nvim/misc1.h" // For line_breakcheck #include "nvim/os/fileio.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 69732f1a7d..d1275d6512 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -141,7 +141,7 @@ typedef struct { dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. partial_T *v_partial; ///< Closure: function with args. blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL. - } vval; ///< Actual value. + } vval; ///< Actual value. } typval_T; /// Values for (struct dictvar_S).dv_scope diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 6fe75e5e1f..eb241eb8ae 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -17,7 +17,6 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" -#include "nvim/misc1.h" #include "nvim/os/input.h" #include "nvim/regexp.h" #include "nvim/search.h" @@ -1818,7 +1817,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, if (name != NULL) { name = vim_strsave(name); *pp = (char_u *)end; - if (strncmp((char *)name, "<SNR>", 5) == 0) { + if (STRNCMP(name, "<SNR>", 5) == 0) { // Change "<SNR>" to the byte sequence. name[0] = K_SPECIAL; name[1] = KS_EXTRA; @@ -2141,7 +2140,7 @@ void ex_function(exarg_T *eap) } // find extra arguments "range", "dict", "abort" and "closure" - for (;; ) { + for (;;) { p = skipwhite(p); if (STRNCMP(p, "range", 5) == 0) { flags |= FC_RANGE; @@ -2204,7 +2203,7 @@ void ex_function(exarg_T *eap) indent = 2; nesting = 0; - for (;; ) { + for (;;) { if (KeyTyped) { msg_scroll = true; saved_wait_return = false; diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index f070c8179f..3c43d1f98d 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -12,7 +12,7 @@ #include "nvim/event/rstream.h" #include "nvim/log.h" #include "nvim/memory.h" -#include "nvim/misc1.h" +#include "nvim/main.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 145fe33284..c0cb17fa61 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -39,6 +39,7 @@ #include "nvim/getchar.h" #include "nvim/highlight.h" #include "nvim/indent.h" +#include "nvim/input.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/mark.h" @@ -46,7 +47,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -303,11 +303,10 @@ void ex_align(exarg_T *eap) * Now try to move the line as much as possible to * the right. Stop when it moves too far. */ - do{ + do { (void)set_indent(++new_indent, 0); - } - while (linelen(NULL) <= width); - --new_indent; + } while (linelen(NULL) <= width); + new_indent--; break; } --new_indent; @@ -774,7 +773,7 @@ void ex_retab(exarg_T *eap) col = 0; vcol = 0; did_undo = false; - for (;; ) { + for (;;) { if (ascii_iswhite(ptr[col])) { if (!got_tab && num_spaces == 0) { // First consecutive white-space @@ -1440,7 +1439,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, _("%" PRId64 " lines filtered"), (int64_t)linecount); if (msg(msg_buf) && !msg_scroll) { // save message to display it after redraw - set_keep_msg((char_u *)msg_buf, 0); + set_keep_msg(msg_buf, 0); } } else { msgmore((long)linecount); @@ -1457,7 +1456,7 @@ error: filterend: if (curbuf != old_curbuf) { - --no_wait_return; + no_wait_return--; emsg(_("E135: *Filter* Autocommands must not change current buffer")); } if (itmp != NULL) { @@ -2094,7 +2093,7 @@ void do_wqall(exarg_T *eap) } if (buf->b_ffname == NULL) { semsg(_("E141: No file name for buffer %" PRId64), (int64_t)buf->b_fnum); - ++error; + error++; } else if (check_readonly(&eap->forceit, buf) || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname, FALSE) == FAIL) { @@ -2120,17 +2119,15 @@ void do_wqall(exarg_T *eap) } } -/* - * Check the 'write' option. - * Return TRUE and give a message when it's not st. - */ -int not_writing(void) +// Check the 'write' option. +// Return true and give a message when it's not st. +bool not_writing(void) { if (p_write) { - return FALSE; + return false; } emsg(_("E142: File not written: Writing is disabled by 'write' option")); - return TRUE; + return true; } /* @@ -2150,7 +2147,7 @@ static int check_readonly(int *forceit, buf_T *buf) if (buf->b_p_ro) { dialog_msg(buff, - _( "'readonly' option is set for \"%s\".\nDo you wish to write anyway?"), + _("'readonly' option is set for \"%s\".\nDo you wish to write anyway?"), buf->b_fname); } else { dialog_msg(buff, @@ -2944,8 +2941,8 @@ void ex_append(exarg_T *eap) State |= LANGMAP; } - for (;; ) { - msg_scroll = TRUE; + for (;;) { + msg_scroll = true; need_wait_return = false; if (curbuf->b_p_ai) { if (append_indent >= 0) { @@ -3131,7 +3128,7 @@ void ex_z(exarg_T *eap) // the number of '-' and '+' multiplies the distance if (*kind == '-' || *kind == '+') { - for (x = kind + 1; *x == *kind; ++x) { + for (x = kind + 1; *x == *kind; x++) { } } @@ -3214,26 +3211,24 @@ void ex_z(exarg_T *eap) ex_no_reprint = true; } -/* - * Check if the secure flag is set (.exrc or .vimrc in current directory). - * If so, give an error message and return TRUE. - * Otherwise, return FALSE. - */ -int check_secure(void) +// Check if the secure flag is set (.exrc or .vimrc in current directory). +// If so, give an error message and return true. +// Otherwise, return false. +bool check_secure(void) { if (secure) { secure = 2; emsg(_(e_curdir)); - return TRUE; + return true; } // In the sandbox more things are not allowed, including the things // disallowed in secure mode. if (sandbox != 0) { emsg(_(e_sandbox)); - return TRUE; + return true; } - return FALSE; + return false; } /// Previous substitute replacement string @@ -3280,7 +3275,7 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, char_u *cm // TODO(vim): find a generic solution to make line-joining operations more // efficient, avoid allocating a string that grows in size. if (pat != NULL - && strcmp((const char *)pat, "\\n") == 0 + && STRCMP(pat, "\\n") == 0 && *sub == NUL && (*cmd == NUL || (cmd[1] == NUL && (*cmd == 'g' @@ -3719,7 +3714,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle * 4. if subflags.do_all is set, find next match * 5. break if there isn't another match in this line */ - for (;; ) { + for (;;) { SubResult current_match = { .start = { 0, 0 }, .end = { 0, 0 }, @@ -4468,7 +4463,7 @@ bool do_sub_msg(bool count_only) (int64_t)sub_nsubs, (int64_t)sub_nlines); if (msg(msg_buf)) { // save message to display it after redraw - set_keep_msg((char_u *)msg_buf, 0); + set_keep_msg(msg_buf, 0); } return true; } @@ -5030,9 +5025,9 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool // When the string starting with "expr-" and containing '?' and matches // the table, it is taken literally (but ~ is escaped). Otherwise '?' // is recognized as a wildcard. - for (i = (int)ARRAY_SIZE(expr_table); --i >= 0; ) { + for (i = (int)ARRAY_SIZE(expr_table); --i >= 0;) { if (STRCMP(arg + 5, expr_table[i]) == 0) { - for (int si = 0, di = 0; ; si++) { + for (int si = 0, di = 0;; si++) { if (arg[si] == '~') { d[di++] = '\\'; } @@ -5047,7 +5042,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool } else { // Recognize a few exceptions to the rule. Some strings that contain // '*' with "star". Otherwise '*' is recognized as a wildcard. - for (i = (int)ARRAY_SIZE(mtable); --i >= 0; ) { + for (i = (int)ARRAY_SIZE(mtable); --i >= 0;) { if (STRCMP(arg, mtable[i]) == 0) { STRCPY(d, rtable[i]); break; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 787b3f07b2..33f9477608 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -35,12 +35,12 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/os/fs_defs.h" +#include "nvim/os/input.h" #include "nvim/os/shell.h" #include "nvim/os_unix.h" #include "nvim/path.h" @@ -410,7 +410,7 @@ static void script_dump_profile(FILE *fd) } else { // Keep going till the end of file, so that trailing // continuation lines are listed. - for (int i = 0; ; i++) { + for (int i = 0;; i++) { if (vim_fgets(IObuff, IOSIZE, sfd)) { break; } @@ -2505,7 +2505,7 @@ static char_u *get_one_sourceline(struct source_cookie *sp) // Loop until there is a finished line (or end-of-file). sp->sourcing_lnum++; - for (;; ) { + for (;;) { // make room to read at least 120 (more) characters ga_grow(&ga, 120); buf = (char_u *)ga.ga_data; diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 6e5dd144ad..ea899b660b 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -135,10 +135,10 @@ typedef struct { // Flags for the cs_lflags item in cstack_T. enum { - CSL_HAD_LOOP = 1, // just found ":while" or ":for" + CSL_HAD_LOOP = 1, // just found ":while" or ":for" CSL_HAD_ENDLOOP = 2, // just found ":endwhile" or ":endfor" - CSL_HAD_CONT = 4, // just found ":continue" - CSL_HAD_FINA = 8, // just found ":finally" + CSL_HAD_CONT = 4, // just found ":continue" + CSL_HAD_FINA = 8, // just found ":finally" }; /// Arguments used for Ex commands. diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 499c6ed4f9..9f0f8d93a3 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -39,6 +39,7 @@ #include "nvim/globals.h" #include "nvim/hardcopy.h" #include "nvim/if_cscope.h" +#include "nvim/input.h" #include "nvim/keymap.h" #include "nvim/lua/executor.h" #include "nvim/main.h" @@ -48,7 +49,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -68,6 +68,7 @@ #include "nvim/spell.h" #include "nvim/spellfile.h" #include "nvim/strings.h" +#include "nvim/state.h" #include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/terminal.h" @@ -196,6 +197,7 @@ void do_exmode(void) exmode_active = true; State = NORMAL; + trigger_modechanged(); // When using ":global /pat/ visual" and then "Q" we return to continue // the :global command. @@ -432,9 +434,9 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) if (next_cmdline == NULL && !force_abort && cstack.cs_idx < 0 - && !(getline_is_func && - func_has_abort(real_cookie))) { - did_emsg = FALSE; + && !(getline_is_func + && func_has_abort(real_cookie))) { + did_emsg = false; } /* @@ -818,7 +820,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // of interrupts or errors to exceptions, and ensure that no more // commands are executed. if (current_exception) { - void *p = NULL; + char *p = NULL; char_u *saved_sourcing_name; int saved_sourcing_lnum; struct msglist *messages = NULL; @@ -835,7 +837,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) vim_snprintf((char *)IObuff, IOSIZE, _("E605: Exception not caught: %s"), current_exception->value); - p = vim_strsave(IObuff); + p = (char *)vim_strsave(IObuff); break; case ET_ERROR: messages = current_exception->messages; @@ -2065,7 +2067,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) eap->save_msg_silent = -1; // Repeat until no more command modifiers are found. - for (;; ) { + for (;;) { while (*eap->cmd == ' ' || *eap->cmd == '\t' || *eap->cmd == ':') { @@ -2647,7 +2649,7 @@ static char_u *find_command(exarg_T *eap, int *full) const int c2 = len == 1 ? NUL : eap->cmd[1]; if (command_count != CMD_SIZE) { - iemsg((char *)_("E943: Command table needs to be updated, run 'make'")); + iemsg(_("E943: Command table needs to be updated, run 'make'")); getout(1); } @@ -2715,8 +2717,8 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int * * Look for buffer-local user commands first, then global ones. */ gap = &curbuf->b_ucmds; - for (;; ) { - for (j = 0; j < gap->ga_len; ++j) { + for (;;) { + for (j = 0; j < gap->ga_len; j++) { uc = USER_CMD_GA(gap, j); cp = eap->cmd; np = uc->uc_name; @@ -4071,7 +4073,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in } } - for (;; ) { + for (;;) { cmd = skipwhite(cmd); if (*cmd != '-' && *cmd != '+' && !ascii_isdigit(*cmd)) { break; @@ -5326,8 +5328,8 @@ static void uc_list(char_u *name, size_t name_len) garray_T *gap = (cmdwin_type != 0 && get_cmdline_type() == NUL) ? &prevwin->w_buffer->b_ucmds : &curbuf->b_ucmds; - for (;; ) { - for (i = 0; i < gap->ga_len; ++i) { + for (;;) { + for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); a = cmd->uc_argt; @@ -5713,8 +5715,8 @@ static void ex_delcommand(exarg_T *eap) garray_T *gap; gap = &curbuf->b_ucmds; - for (;; ) { - for (i = 0; i < gap->ga_len; ++i) { + for (;;) { + for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); cmp = STRCMP(eap->arg, cmd->uc_name); if (cmp <= 0) { @@ -6153,12 +6155,12 @@ static void do_ucmd(exarg_T *eap) * Second round: copy result into "buf". */ buf = NULL; - for (;; ) { + for (;;) { p = cmd->uc_rep; // source q = buf; // destination totlen = 0; - for (;; ) { + for (;;) { start = vim_strchr(p, '<'); if (start != NULL) { end = vim_strchr(start + 1, '>'); @@ -6184,7 +6186,7 @@ static void do_ucmd(exarg_T *eap) } } - // break if there no <item> is found + // break if no <item> is found if (start == NULL || end == NULL) { break; } @@ -7753,6 +7755,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) abort(); } + last_chdir_reason = NULL; shorten_fnames(true); if (trigger_dirchanged) { @@ -7870,7 +7873,9 @@ static void ex_pwd(exarg_T *eap) #endif if (p_verbose > 0) { char *context = "global"; - if (curwin->w_localdir != NULL) { + if (last_chdir_reason != NULL) { + context = last_chdir_reason; + } else if (curwin->w_localdir != NULL) { context = "window"; } else if (curtab->tp_localdir != NULL) { context = "tabpage"; @@ -8334,9 +8339,7 @@ static void ex_redir(exarg_T *eap) if (var_redir_start(skipwhite(arg), append) == OK) { redir_vname = 1; } - } - // TODO: redirect to a buffer - else { + } else { // TODO(vim): redirect to a buffer semsg(_(e_invarg2), eap->arg); } } @@ -8983,8 +8986,8 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) /// @return an allocated string if a valid match was found. /// Returns NULL if no match was found. "usedlen" then still contains the /// number of characters to skip. -char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, - char **errormsg, int *escaped) +char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg, + int *escaped) { int i; char_u *s; @@ -9130,7 +9133,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum // postponed to avoid a delay when <afile> is not used. result = (char_u *)FullName_save((char *)autocmd_fname, false); // Copy into `autocmd_fname`, don't reassign it. #8165 - xstrlcpy((char *)autocmd_fname, (char *)result, MAXPATHL); + STRLCPY(autocmd_fname, result, MAXPATHL); xfree(result); } result = autocmd_fname; @@ -9252,7 +9255,7 @@ static char_u *arg_all(void) * first time: compute the total length * second time: concatenate the names */ - for (;; ) { + for (;;) { len = 0; for (idx = 0; idx < ARGCOUNT; ++idx) { p = alist_name(&ARGLIST[idx]); @@ -9315,7 +9318,7 @@ char_u *expand_sfile(char_u *arg) result = vim_strsave(arg); - for (p = result; *p; ) { + for (p = result; *p;) { if (STRNCMP(p, "<sfile>", 7) != 0) { ++p; } else { @@ -9461,7 +9464,7 @@ static void ex_filetype(exarg_T *eap) } // Accept "plugin" and "indent" in any order. - for (;; ) { + for (;;) { if (STRNCMP(arg, "plugin", 6) == 0) { plugin = true; arg = skipwhite(arg + 6); diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index cfc8eb7316..b1c59a607c 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -437,7 +437,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int } } } else { - *should_free = FALSE; + *should_free = false; ret = value; } @@ -566,7 +566,7 @@ static void discard_exception(except_T *excp, bool was_finished) } else { verbose_leave(); } - xstrlcpy((char *)IObuff, (const char *)saved_IObuff, IOSIZE); + STRLCPY(IObuff, saved_IObuff, IOSIZE); xfree(saved_IObuff); } if (excp->type != ET_INTERRUPT) { @@ -888,11 +888,10 @@ void ex_endif(exarg_T *eap) */ void ex_else(exarg_T *eap) { - int skip; int result; cstack_T *const cstack = eap->cstack; - skip = CHECK_SKIP; + bool skip = CHECK_SKIP; if (cstack->cs_idx < 0 || (cstack->cs_flags[cstack->cs_idx] @@ -902,14 +901,14 @@ void ex_else(exarg_T *eap) return; } eap->errmsg = N_("E582: :elseif without :if"); - skip = TRUE; + skip = true; } else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) { if (eap->cmdidx == CMD_else) { eap->errmsg = N_("E583: multiple :else"); return; } eap->errmsg = N_("E584: :elseif after :else"); - skip = TRUE; + skip = true; } // if skipping or the ":if" was TRUE, reset ACTIVE, otherwise set it @@ -917,7 +916,7 @@ void ex_else(exarg_T *eap) if (eap->errmsg == NULL) { cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; } - skip = TRUE; // don't evaluate an ":elseif" + skip = true; // don't evaluate an ":elseif" } else { cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE; } @@ -932,7 +931,7 @@ void ex_else(exarg_T *eap) // later on. if (!skip && dbg_check_skipped(eap) && got_int) { (void)do_intthrow(cstack); - skip = TRUE; + skip = true; } if (eap->cmdidx == CMD_elseif) { @@ -1322,9 +1321,9 @@ void ex_try(exarg_T *eap) void ex_catch(exarg_T *eap) { int idx = 0; - int give_up = FALSE; - int skip = FALSE; - int caught = FALSE; + bool give_up = false; + bool skip = false; + bool caught = false; char_u *end; char_u save_char = 0; char_u *save_cpo; @@ -1335,13 +1334,13 @@ void ex_catch(exarg_T *eap) if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = N_("E603: :catch without :try"); - give_up = TRUE; + give_up = true; } else { if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { // Report what's missing if the matching ":try" is not in its // finally clause. eap->errmsg = get_end_emsg(cstack); - skip = TRUE; + skip = true; } for (idx = cstack->cs_idx; idx > 0; --idx) { if (cstack->cs_flags[idx] & CSF_TRY) { @@ -1352,7 +1351,7 @@ void ex_catch(exarg_T *eap) // Give up for a ":catch" after ":finally" and ignore it. // Just parse. eap->errmsg = N_("E604: :catch after :finally"); - give_up = TRUE; + give_up = true; } else { rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); @@ -1425,7 +1424,7 @@ void ex_catch(exarg_T *eap) // CTRL-C while matching should abort it. // prev_got_int = got_int; - got_int = FALSE; + got_int = false; caught = vim_regexec_nl(®match, (char_u *)current_exception->value, (colnr_T)0); got_int |= prev_got_int; @@ -1602,8 +1601,7 @@ void ex_finally(exarg_T *eap) void ex_endtry(exarg_T *eap) { int idx; - int skip; - int rethrow = FALSE; + bool rethrow = false; int pending = CSTP_NONE; void *rettv = NULL; cstack_T *const cstack = eap->cstack; @@ -1621,20 +1619,19 @@ void ex_endtry(exarg_T *eap) // made inactive by a ":continue", ":break", ":return", or ":finish" in // the finally clause. The latter case need not be tested since then // anything pending has already been discarded. - skip = (did_emsg || got_int || current_exception - || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)); + bool skip = did_emsg || got_int || current_exception + || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); // Find the matching ":try" and report what's missing. idx = cstack->cs_idx; - do{ - --idx; - } - while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); + do { + idx--; + } while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); - skip = TRUE; + skip = true; /* * If an exception is being thrown, discard it to prevent it from @@ -1677,7 +1674,7 @@ void ex_endtry(exarg_T *eap) // before the ":endtry". That is, throw an interrupt exception and // set "skip" and "rethrow". if (got_int) { - skip = TRUE; + skip = true; (void)do_intthrow(cstack); // The do_intthrow() call may have reset current_exception or // cstack->cs_pending[idx]. @@ -2091,7 +2088,7 @@ int has_loop_cmd(char_u *p) int len; // skip modifiers, white space and ':' - for (;; ) { + for (;;) { while (*p == ' ' || *p == '\t' || *p == ':') { ++p; } diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 25b30b2805..15da4b2d60 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -17,8 +17,8 @@ #define CSF_THROWN 0x0400 // exception thrown to this try conditional #define CSF_CAUGHT 0x0800 // exception caught by this try conditional #define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try" -/* Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset - * (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. */ +// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset +// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. /* * What's pending for being reactivated at the ":endtry" of this try diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9ccbdbaf58..ba2238ace2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -48,7 +48,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/ops.h" @@ -880,7 +879,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) TryState tstate; Error err = ERROR_INIT; bool tl_ret = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); firstcbuf[1] = 0; @@ -894,7 +894,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); tl_ret = try_leave(&tstate, &err); @@ -906,6 +906,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } tl_ret = true; } + trigger_modechanged(); state_enter(&s->state); @@ -924,7 +925,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (tv_dict_get_number(dict, "abort") != 0) { s->gotesc = 1; } - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); } cmdmsg_rl = false; @@ -1665,7 +1666,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ static void command_line_next_histidx(CommandLineState *s, bool next_match) { int j = (int)STRLEN(s->lookfor); - for (;; ) { + for (;;) { // one step backwards if (!next_match) { if (s->hiscnt == hislen) { @@ -2289,7 +2290,8 @@ static int command_line_changed(CommandLineState *s) if (has_event(EVENT_CMDLINECHANGED)) { TryState tstate; Error err = ERROR_INIT; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-'); @@ -2303,7 +2305,7 @@ static int command_line_changed(CommandLineState *s) apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf, (char_u *)firstcbuf, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); bool tl_ret = try_leave(&tstate, &err); if (!tl_ret && ERROR_SET(&err)) { @@ -2488,27 +2490,25 @@ char *get_text_locked_msg(void) } /// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and -/// return TRUE when it is and give an error message. -int curbuf_locked(void) +/// return true when it is and give an error message. +bool curbuf_locked(void) { if (curbuf->b_ro_locked > 0) { emsg(_("E788: Not allowed to edit another buffer now")); - return TRUE; + return true; } return allbuf_locked(); } -/* - * Check if "allbuf_lock" is set and return TRUE when it is and give an error - * message. - */ -int allbuf_locked(void) +// Check if "allbuf_lock" is set and return true when it is and give an error +// message. +bool allbuf_locked(void) { if (allbuf_lock > 0) { emsg(_("E811: Not allowed to change buffer information now")); - return TRUE; + return true; } - return FALSE; + return false; } static int cmdline_charsize(int idx) @@ -2686,7 +2686,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline, viml_parser_destroy(&pstate); kv_resize(ret_ccline_colors->colors, kv_size(colors)); size_t prev_end = 0; - for (size_t i = 0 ; i < kv_size(colors) ; i++) { + for (size_t i = 0; i < kv_size(colors); i++) { const ParserHighlightChunk chunk = kv_A(colors, i); assert(chunk.start.col < INT_MAX); assert(chunk.end_col < INT_MAX); @@ -2697,7 +2697,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline, .attr = 0, })); } - const int id = syn_name2id((const char_u *)chunk.group); + const int id = syn_name2id(chunk.group); const int attr = (id == 0 ? 0 : syn_id2attr(id)); kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { .start = (int)chunk.start.col, @@ -2899,7 +2899,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) if (group == NULL) { goto color_cmdline_error; } - const int id = syn_name2id((char_u *)group); + const int id = syn_name2id(group); const int attr = (id == 0 ? 0 : syn_id2attr(id)); kv_push(ccline_colors->colors, ((CmdlineColorChunk) { .start = (int)start, @@ -3466,7 +3466,7 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) int len; // Locate start of last word in the cmd buffer. - for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff; ) { + for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff;) { len = utf_head_off(ccline.cmdbuff, w - 1) + 1; if (!vim_iswordc(utf_ptr2char(w - len))) { break; @@ -4386,7 +4386,7 @@ static int showmatches(expand_T *xp, int wildmenu) msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D)); break; } - for (j = maxlen - lastlen; --j >= 0; ) { + for (j = maxlen - lastlen; --j >= 0;) { msg_putchar(' '); } if (xp->xp_context == EXPAND_FILES @@ -4457,7 +4457,7 @@ char_u *sm_gettail(char_u *s, bool eager) char_u *t = s; int had_sep = FALSE; - for (p = s; *p != NUL; ) { + for (p = s; *p != NUL;) { if (vim_ispathsep(*p) #ifdef BACKSLASH_IN_FILENAME && !rem_backslash(p) @@ -4989,13 +4989,13 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** // When expanding a function name starting with s:, match the <SNR>nr_ // prefix. - char_u *tofree = NULL; + char *tofree = NULL; if (xp->xp_context == EXPAND_USER_FUNC && STRNCMP(pat, "^s:", 3) == 0) { const size_t len = STRLEN(pat) + 20; tofree = xmalloc(len); - snprintf((char *)tofree, len, "^<SNR>\\d\\+_%s", pat + 3); - pat = tofree; + snprintf(tofree, len, "^<SNR>\\d\\+_%s", pat + 3); + pat = (char_u *)tofree; } if (xp->xp_context == EXPAND_LUA) { @@ -5219,7 +5219,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int ga_init(&ga, (int)sizeof(char *), 10); hashtab_T found_ht; hash_init(&found_ht); - for (s = path; ; s = e) { + for (s = path;; s = e) { e = vim_strchr(s, ENV_SEPCHAR); if (e == NULL) { e = s + STRLEN(s); @@ -6547,6 +6547,7 @@ static int open_cmdwin(void) cmdmsg_rl = save_cmdmsg_rl; State = save_State; + trigger_modechanged(); setmouse(); return cmdwin_result; diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 9aa8cc0107..a37cad9f2d 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -28,7 +28,6 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/keymap.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os/input.h" @@ -345,8 +344,8 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr // Load the file. // if (wp->w_buffer->b_ffname != NULL - && (!bt_nofile(wp->w_buffer) || - wp->w_buffer->terminal)) { + && (!bt_nofile(wp->w_buffer) + || wp->w_buffer->terminal)) { // Editing a file in this buffer: use ":edit file". // This may have side effects! (e.g., compressed or network file). // @@ -431,8 +430,8 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr // if ((*flagp & SSOP_FOLDS) && wp->w_buffer->b_ffname != NULL - && (bt_normal(wp->w_buffer) || - bt_help(wp->w_buffer))) { + && (bt_normal(wp->w_buffer) + || bt_help(wp->w_buffer))) { if (put_folds(fd, wp) == FAIL) { return FAIL; } diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 36257fefb3..d31021b3ef 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -57,7 +57,6 @@ #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/fs_defs.h" #include "nvim/os/input.h" @@ -605,9 +604,9 @@ char_u *vim_findfile(void *search_ctx_arg) } // upward search loop - for (;; ) { + for (;;) { // downward search loop - for (;; ) { + for (;;) { // check if user user wants to stop the search os_breakcheck(); if (got_int) { @@ -829,7 +828,7 @@ char_u *vim_findfile(void *search_ctx_arg) } else { suf = curbuf->b_p_sua; } - for (;; ) { + for (;;) { // if file exists and we didn't already find it if ((path_with_url((char *)file_path) || (os_path_exists(file_path) @@ -1500,7 +1499,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first /* When the file doesn't exist, try adding parts of * 'suffixesadd'. */ buf = suffixes; - for (;; ) { + for (;;) { if ( (os_path_exists(NameBuff) && (find_what == FINDFILE_BOTH @@ -1530,7 +1529,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first did_findfile_init = FALSE; } - for (;; ) { + for (;;) { if (did_findfile_init) { file_name = vim_findfile(fdip_search_ctx); if (file_name != NULL) { @@ -1603,7 +1602,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) recursive = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char buf[8]; switch (scope) { @@ -1648,7 +1648,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index cf4037308b..f8cf341836 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -30,12 +30,12 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/iconv.h" +#include "nvim/input.h" #include "nvim/mbyte.h" #include "nvim/memfile.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" @@ -100,8 +100,8 @@ struct bw_info { char_u bw_rest[CONV_RESTLEN]; // not converted bytes int bw_restlen; // nr of bytes in bw_rest[] int bw_first; // first write call - char_u *bw_conv_buf; // buffer for writing converted chars - int bw_conv_buflen; // size of bw_conv_buf + char_u *bw_conv_buf; // buffer for writing converted chars + size_t bw_conv_buflen; // size of bw_conv_buf int bw_conv_error; // set for conversion error linenr_T bw_conv_error_lnum; // first line with error or zero linenr_T bw_start_lnum; // line number at start of buffer @@ -125,7 +125,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) } add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)name); // Avoid an over-long translation to cause trouble. - xstrlcat((char *)IObuff, (const char *)s, IOSIZE); + STRLCAT(IObuff, s, IOSIZE); // For the first message may have to start a new line. // For further ones overwrite the previous one, reset msg_scroll before // calling filemess(). @@ -995,7 +995,7 @@ retry: long tlen; tlen = 0; - for (;; ) { + for (;;) { p = ml_get(read_buf_lnum) + read_buf_col; n = (int)STRLEN(p); if ((int)tlen + n + 1 > size) { @@ -1360,6 +1360,10 @@ retry: u8c += (unsigned)(*--p) << 16; u8c += (unsigned)(*--p) << 24; } + // Replace characters over INT_MAX with Unicode replacement character + if (u8c > INT_MAX) { + u8c = 0xfffd; + } } else { // UTF-8 if (*--p < 0x80) { u8c = *p; @@ -1847,7 +1851,7 @@ failed: msg_scrolled_ign = true; if (!read_stdin && !read_buffer) { - p = (char_u *)msg_trunc_attr((char *)IObuff, FALSE, 0); + p = (char_u *)msg_trunc_attr((char *)IObuff, false, 0); } if (read_stdin || read_buffer || restart_edit != 0 @@ -1857,7 +1861,7 @@ failed: // - When restart_edit is set (otherwise there will be a delay before // redrawing). // - When the screen was scrolled but there is no wait-return prompt. - set_keep_msg(p, 0); + set_keep_msg((char *)p, 0); } msg_scrolled_ign = false; } @@ -3125,7 +3129,7 @@ nobackup: // If conversion is taking place, we may first pretend to write and check // for conversion errors. Then loop again to write for real. // When not doing conversion this writes for real right away. - for (checking_conversion = true; ; checking_conversion = false) { + for (checking_conversion = true;; checking_conversion = false) { // There is no need to check conversion when: // - there is no conversion // - we make a backup file, that can be restored in case of conversion @@ -3536,7 +3540,7 @@ restore_backup: } } - set_keep_msg((char_u *)msg_trunc_attr((char *)IObuff, FALSE, 0), 0); + set_keep_msg(msg_trunc_attr((char *)IObuff, false, 0), 0); } /* When written everything correctly: reset 'modified'. Unless not @@ -4265,8 +4269,8 @@ static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags) len = 4; } else if (flags == (FIO_UCS2 | FIO_ENDIAN_L)) { name = "ucs-2le"; // FF FE - } else if (flags == FIO_ALL || - flags == (FIO_UTF16 | FIO_ENDIAN_L)) { + } else if (flags == FIO_ALL + || flags == (FIO_UTF16 | FIO_ENDIAN_L)) { // utf-16le is preferred, it also works for ucs-2le text name = "utf-16le"; // FF FE } @@ -4811,8 +4815,8 @@ int check_timestamps(int focus) } if (!stuff_empty() || global_busy || !typebuf_typed() - || autocmd_busy || curbuf->b_ro_locked > 0 || - allbuf_lock > 0) { + || autocmd_busy || curbuf->b_ro_locked > 0 + || allbuf_lock > 0) { need_check_timestamps = true; // check later } else { no_wait_return++; @@ -5727,7 +5731,7 @@ long read_eintr(int fd, void *buf, size_t bufsize) { long ret; - for (;; ) { + for (;;) { ret = read(fd, buf, bufsize); if (ret >= 0 || errno != EINTR) { break; diff --git a/src/nvim/fold.c b/src/nvim/fold.c index daf0df9326..b1d4321d4c 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -28,10 +28,10 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/os/input.h" #include "nvim/plines.h" #include "nvim/screen.h" #include "nvim/strings.h" @@ -202,7 +202,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp * Recursively search for a fold that contains "lnum". */ garray_T *gap = &win->w_folds; - for (;; ) { + for (;;) { if (!foldFind(gap, lnum_rel, &fp)) { break; } @@ -455,7 +455,7 @@ void foldOpenCursor(void) checkupdate(curwin); if (hasAnyFolding(curwin)) { - for (;; ) { + for (;;) { done = DONE_NOTHING; (void)setManualFold(curwin->w_cursor, true, false, &done); if (!(done & DONE_ACTION)) { @@ -716,7 +716,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const garray_T *found_ga = NULL; linenr_T lnum_off = 0; bool use_level = false; - for (;; ) { + for (;;) { if (!foldFind(gap, lnum - lnum_off, &fp)) { break; } @@ -897,7 +897,7 @@ int foldMoveTo(const bool updown, const int dir, const long count) linenr_T lnum_found = curwin->w_cursor.lnum; int level = 0; bool last = false; - for (;; ) { + for (;;) { if (!foldFind(gap, curwin->w_cursor.lnum - lnum_off, &fp)) { if (!updown || gap->ga_len == 0) { break; @@ -1155,7 +1155,7 @@ static int foldLevelWin(win_T *wp, linenr_T lnum) // Recursively search for a fold that contains "lnum". gap = &wp->w_folds; - for (;; ) { + for (;;) { if (!foldFind(gap, lnum_rel, &fp)) { break; } @@ -1265,7 +1265,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu * Find the fold, open or close it. */ gap = &wp->w_folds; - for (;; ) { + for (;;) { if (!foldFind(gap, lnum, &fp)) { // If there is a following fold, continue there next time. if (fp != NULL && fp < (fold_T *)gap->ga_data + gap->ga_len) { @@ -1928,7 +1928,7 @@ void foldtext_cleanup(char_u *str) } parseMarker(curwin); - for (s = str; *s != NUL; ) { + for (s = str; *s != NUL;) { size_t len = 0; if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) { len = foldstartmarkerlen; @@ -2603,7 +2603,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level, } // delete following folds that end before the current line - for (;; ) { + for (;;) { fp2 = fp + 1; if (fp2 >= (fold_T *)gap->ga_data + gap->ga_len || fp2->fd_top > flp->lnum) { diff --git a/src/nvim/fold.h b/src/nvim/fold.h index a96ea8a039..6b29214760 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -14,10 +14,10 @@ */ typedef struct foldinfo { linenr_T fi_lnum; // line number where fold starts - int fi_level; /* level of the fold; when this is zero the - other fields are invalid */ - int fi_low_level; /* lowest fold level that starts in the same - line */ + int fi_level; // level of the fold; when this is zero the + // other fields are invalid + int fi_low_level; // lowest fold level that starts in the same + // line long fi_lines; } foldinfo_T; diff --git a/src/nvim/generators/gen_char_blob.lua b/src/nvim/generators/gen_char_blob.lua index a7dad50d48..3ec1ff2caf 100644 --- a/src/nvim/generators/gen_char_blob.lua +++ b/src/nvim/generators/gen_char_blob.lua @@ -1,12 +1,26 @@ if arg[1] == '--help' then print('Usage:') - print(' '..arg[0]..' target source varname [source varname]...') + print(' '..arg[0]..' [-c] target source varname [source varname]...') print('') print('Generates C file with big uint8_t blob.') print('Blob will be stored in a static const array named varname.') os.exit() end +-- Recognized options: +-- -c compile Lua bytecode +local options = {} + +while true do + local opt = string.match(arg[1], "^-(%w)") + if not opt then + break + end + + options[opt] = true + table.remove(arg, 1) +end + assert(#arg >= 3 and (#arg - 1) % 2 == 0) local target_file = arg[1] or error('Need a target file') @@ -14,6 +28,7 @@ local target = io.open(target_file, 'w') target:write('#include <stdint.h>\n\n') +local warn_on_missing_compiler = true local varnames = {} for argi = 2, #arg, 2 do local source_file = arg[argi] @@ -23,11 +38,26 @@ for argi = 2, #arg, 2 do end varnames[varname] = source_file - local source = io.open(source_file, 'r') - or error(string.format("source_file %q doesn't exist", source_file)) - target:write(('static const uint8_t %s[] = {\n'):format(varname)) + local output + if options.c then + local luac = os.getenv("LUAC_PRG") + if luac and luac ~= "" then + output = io.popen(luac:format(source_file), "r"):read("*a") + elseif warn_on_missing_compiler then + print("LUAC_PRG is missing, embedding raw source") + warn_on_missing_compiler = false + end + end + + if not output then + local source = io.open(source_file, "r") + or error(string.format("source_file %q doesn't exist", source_file)) + output = source:read("*a") + source:close() + end + local num_bytes = 0 local MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line target:write(' ') @@ -41,19 +71,13 @@ for argi = 2, #arg, 2 do end end - for line in source:lines() do - for i = 1, string.len(line) do - local byte = line:byte(i) - assert(byte ~= 0) - target:write(string.format(' %3u,', byte)) - increase_num_bytes() - end - target:write(string.format(' %3u,', string.byte('\n', 1))) + for i = 1, string.len(output) do + local byte = output:byte(i) + target:write(string.format(' %3u,', byte)) increase_num_bytes() end - target:write(' 0};\n') - source:close() + target:write(' 0};\n') end target:close() diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 5267b313c7..6b1150cefa 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -29,6 +29,7 @@ #include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/input.h" #include "nvim/keymap.h" #include "nvim/lua/executor.h" #include "nvim/main.h" @@ -36,7 +37,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -458,6 +458,15 @@ void flush_buffers(flush_buffers_T flush_typeahead) } } +/// flush map and typeahead buffers and give a warning for an error +void beep_flush(void) +{ + if (emsg_silent == 0) { + flush_buffers(FLUSH_MINIMAL); + vim_beep(BO_ERROR); + } +} + /* * The previous contents of the redo buffer is kept in old_redobuffer. * This is used for the CTRL-O <.> command in insert mode. @@ -1453,7 +1462,7 @@ int vgetc(void) } else { mod_mask = 0x0; last_recorded_len = 0; - for (;; ) { // this is done twice if there are modifiers + for (;;) { // this is done twice if there are modifiers bool did_inc = false; if (mod_mask) { // no mapping after modifier has been read no_mapping++; @@ -1680,6 +1689,325 @@ int char_avail(void) return retval != NUL; } +typedef enum { + map_result_fail, // failed, break loop + map_result_get, // get a character from typeahead + map_result_retry, // try to map again + map_result_nomatch // no matching mapping, get char +} map_result_T; + +/// Handle mappings in the typeahead buffer. +/// - When something was mapped, return map_result_retry for recursive mappings. +/// - When nothing mapped and typeahead has a character: return map_result_get. +/// - When there is no match yet, return map_result_nomatch, need to get more +/// typeahead. +static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) +{ + mapblock_T *mp = NULL; + mapblock_T *mp2; + mapblock_T *mp_match; + int mp_match_len = 0; + int max_mlen = 0; + int tb_c1; + int mlen; + int nolmaplen; + int keylen = *keylenp; + int i; + int local_State = get_real_state(); + + // Check for a mappable key sequence. + // Walk through one maphash[] list until we find an entry that matches. + // + // Don't look for mappings if: + // - no_mapping set: mapping disabled (e.g. for CTRL-V) + // - maphash_valid not set: no mappings present. + // - typebuf.tb_buf[typebuf.tb_off] should not be remapped + // - in insert or cmdline mode and 'paste' option set + // - waiting for "hit return to continue" and CR or SPACE typed + // - waiting for a char with --more-- + // - in Ctrl-X mode, and we get a valid char for that mode + tb_c1 = typebuf.tb_buf[typebuf.tb_off]; + if (no_mapping == 0 && maphash_valid + && (no_zero_mapping == 0 || tb_c1 != '0') + && (typebuf.tb_maplen == 0 + || (p_remap + && !(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR)))) + && !(p_paste && (State & (INSERT + CMDLINE))) + && !(State == HITRETURN && (tb_c1 == CAR || tb_c1 == ' ')) + && State != ASKMORE + && State != CONFIRM + && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(tb_c1)) + || ((compl_cont_status & CONT_LOCAL) + && (tb_c1 == Ctrl_N || tb_c1 == Ctrl_P)))) { + if (tb_c1 == K_SPECIAL) { + nolmaplen = 2; + } else { + LANGMAP_ADJUST(tb_c1, !(State & (CMDLINE | INSERT)) && get_real_state() != SELECTMODE); + nolmaplen = 0; + } + // First try buffer-local mappings. + mp = curbuf->b_maphash[MAP_HASH(local_State, tb_c1)]; + mp2 = maphash[MAP_HASH(local_State, tb_c1)]; + if (mp == NULL) { + // There are no buffer-local mappings. + mp = mp2; + mp2 = NULL; + } + // Loop until a partly matching mapping is found or all (local) + // mappings have been checked. + // The longest full match is remembered in "mp_match". + // A full match is only accepted if there is no partly match, so "aa" + // and "aaa" can both be mapped. + mp_match = NULL; + mp_match_len = 0; + for (; mp != NULL; mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next)) { + // Only consider an entry if the first character matches and it is + // for the current state. + // Skip ":lmap" mappings if keys were mapped. + if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State) + && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) { + int nomap = nolmaplen; + int c2; + // find the match length of this mapping + for (mlen = 1; mlen < typebuf.tb_len; mlen++) { + c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; + if (nomap > 0) { + nomap--; + } else if (c2 == K_SPECIAL) { + nomap = 2; + } else { + LANGMAP_ADJUST(c2, true); + } + if (mp->m_keys[mlen] != c2) { + break; + } + } + + // Don't allow mapping the first byte(s) of a multi-byte char. + // Happens when mapping <M-a> and then changing 'encoding'. + // Beware that 0x80 is escaped. + char_u *p1 = mp->m_keys; + char_u *p2 = (char_u *)mb_unescape((const char **)&p1); + + if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len(p2)) { + mlen = 0; + } + + // Check an entry whether it matches. + // - Full match: mlen == keylen + // - Partly match: mlen == typebuf.tb_len + keylen = mp->m_keylen; + if (mlen == keylen || (mlen == typebuf.tb_len && typebuf.tb_len < keylen)) { + char_u *s; + int n; + + // If only script-local mappings are allowed, check if the + // mapping starts with K_SNR. + s = typebuf.tb_noremap + typebuf.tb_off; + if (*s == RM_SCRIPT + && (mp->m_keys[0] != K_SPECIAL + || mp->m_keys[1] != KS_EXTRA + || mp->m_keys[2] != KE_SNR)) { + continue; + } + + // If one of the typed keys cannot be remapped, skip the entry. + for (n = mlen; --n >= 0;) { + if (*s++ & (RM_NONE|RM_ABBR)) { + break; + } + } + if (n >= 0) { + continue; + } + + if (keylen > typebuf.tb_len) { + if (!*timedout && !(mp_match != NULL && mp_match->m_nowait)) { + // break at a partly match + keylen = KEYLEN_PART_MAP; + break; + } + } else if (keylen > mp_match_len + || (keylen == mp_match_len + && mp_match != NULL + && (mp_match->m_mode & LANGMAP) == 0 + && (mp->m_mode & LANGMAP) != 0)) { + // found a longer match + mp_match = mp; + mp_match_len = keylen; + } + } else { + // No match; may have to check for termcode at next character. + if (max_mlen < mlen) { + max_mlen = mlen; + } + } + } + } + + // If no partly match found, use the longest full match. + if (keylen != KEYLEN_PART_MAP) { + mp = mp_match; + keylen = mp_match_len; + } + } + + // Check for match with 'pastetoggle' + if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { + bool match = typebuf_match_len(p_pt, &mlen); + if (match) { + // write chars to script file(s) + if (mlen > typebuf.tb_maplen) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(mlen - typebuf.tb_maplen)); + } + + del_typebuf(mlen, 0); // remove the chars + set_option_value("paste", !p_paste, NULL, 0); + if (!(State & INSERT)) { + msg_col = 0; + msg_row = Rows - 1; + msg_clr_eos(); // clear ruler + } + status_redraw_all(); + redraw_statuslines(); + showmode(); + setcursor(); + *keylenp = keylen; + return map_result_retry; + } + // Need more chars for partly match. + if (mlen == typebuf.tb_len) { + keylen = KEYLEN_PART_KEY; + } else if (max_mlen < mlen) { + // no match, may have to check for termcode at next character + max_mlen = mlen + 1; + } + } + + if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { + // No matching mapping found or found a non-matching mapping that + // matches at least what the matching mapping matched + keylen = 0; + (void)keylen; // suppress clang/dead assignment + // If there was no mapping, use the character from the typeahead + // buffer right here. Otherwise, use the mapping (loop around). + if (mp == NULL) { + *keylenp = keylen; + return map_result_get; // get character from typeahead + } else { + keylen = mp_match_len; + } + } + + // complete match + if (keylen >= 0 && keylen <= typebuf.tb_len) { + char_u *map_str; + int save_m_expr; + int save_m_noremap; + int save_m_silent; + + // Write chars to script file(s). + // Note: :lmap mappings are written *after* being applied. #5658 + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(keylen - typebuf.tb_maplen)); + } + + cmd_silent = (typebuf.tb_silent > 0); + del_typebuf(keylen, 0); // remove the mapped keys + + // Put the replacement string in front of mapstr. + // The depth check catches ":map x y" and ":map y x". + if (++*mapdepth >= p_mmd) { + emsg(_("E223: recursive mapping")); + if (State & CMDLINE) { + redrawcmdline(); + } else { + setcursor(); + } + flush_buffers(FLUSH_MINIMAL); + *mapdepth = 0; // for next one + *keylenp = keylen; + return map_result_fail; + } + + // In Select mode and a Visual mode mapping is used: Switch to Visual + // mode temporarily. Append K_SELECT to switch back to Select mode. + if (VIsual_active && VIsual_select && (mp->m_mode & VISUAL)) { + VIsual_select = false; + (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); + } + + // Copy the values from *mp that are used, because evaluating the + // expression may invoke a function that redefines the mapping, thereby + // making *mp invalid. + save_m_expr = mp->m_expr; + save_m_noremap = mp->m_noremap; + save_m_silent = mp->m_silent; + char_u *save_m_keys = NULL; // only saved when needed + char_u *save_m_str = NULL; // only saved when needed + + // Handle ":map <expr>": evaluate the {rhs} as an + // expression. Also save and restore the command line + // for "normal :". + if (mp->m_expr) { + int save_vgetc_busy = vgetc_busy; + const bool save_may_garbage_collect = may_garbage_collect; + + vgetc_busy = 0; + may_garbage_collect = false; + + save_m_keys = vim_strsave(mp->m_keys); + save_m_str = vim_strsave(mp->m_str); + map_str = eval_map_expr(save_m_str, NUL); + vgetc_busy = save_vgetc_busy; + may_garbage_collect = save_may_garbage_collect; + } else { + map_str = mp->m_str; + } + + // Insert the 'to' part in the typebuf.tb_buf. + // If 'from' field is the same as the start of the 'to' field, don't + // remap the first character (but do allow abbreviations). + // If m_noremap is set, don't remap the whole 'to' part. + if (map_str == NULL) { + i = FAIL; + } else { + int noremap; + + // If this is a LANGMAP mapping, then we didn't record the keys + // at the start of the function and have to record them now. + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { + gotchars(map_str, STRLEN(map_str)); + } + + if (save_m_noremap != REMAP_YES) { + noremap = save_m_noremap; + } else if (STRNCMP(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys, + (size_t)keylen) != 0) { + noremap = REMAP_YES; + } else { + noremap = REMAP_SKIP; + } + i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent); + if (save_m_expr) { + xfree(map_str); + } + } + xfree(save_m_keys); + xfree(save_m_str); + *keylenp = keylen; + if (i == FAIL) { + return map_result_fail; + } + return map_result_retry; + } + + *keylenp = keylen; + return map_result_nomatch; +} + // unget one character (can only be done once!) void vungetc(int c) { @@ -1715,44 +2043,27 @@ void vungetc(int c) static int vgetorpeek(bool advance) { int c, c1; - int keylen; - char_u *s; - mapblock_T *mp; - mapblock_T *mp2; - mapblock_T *mp_match; - int mp_match_len = 0; - bool timedout = false; // waited for more than 1 second - // for mapping to complete + bool timedout = false; // waited for more than 1 second for mapping to complete int mapdepth = 0; // check for recursive mapping bool mode_deleted = false; // set when mode has been deleted - int local_State; - int mlen; - int max_mlen; - int i; int new_wcol, new_wrow; int n; - int nolmaplen; int old_wcol, old_wrow; int wait_tb_len; - /* - * This function doesn't work very well when called recursively. This may - * happen though, because of: - * 1. The call to add_to_showcmd(). char_avail() is then used to check if - * there is a character available, which calls this function. In that - * case we must return NUL, to indicate no character is available. - * 2. A GUI callback function writes to the screen, causing a - * wait_return(). - * Using ":normal" can also do this, but it saves the typeahead buffer, - * thus it should be OK. But don't get a key from the user then. - */ - if (vgetc_busy > 0 - && ex_normal_busy == 0) { + // This function doesn't work very well when called recursively. This may + // happen though, because of: + // 1. The call to add_to_showcmd(). char_avail() is then used to check if + // there is a character available, which calls this function. In that + // case we must return NUL, to indicate no character is available. + // 2. A GUI callback function writes to the screen, causing a + // wait_return(). + // Using ":normal" can also do this, but it saves the typeahead buffer, + // thus it should be OK. But don't get a key from the user then. + if (vgetc_busy > 0 && ex_normal_busy == 0) { return NUL; } - local_State = get_real_state(); - ++vgetc_busy; if (advance) { @@ -1765,9 +2076,7 @@ static int vgetorpeek(bool advance) reg_executing = 0; } do { - /* - * get a character: 1. from the stuffbuffer - */ + // get a character: 1. from the stuffbuffer if (typeahead_char != 0) { c = typeahead_char; if (advance) { @@ -1784,30 +2093,27 @@ static int vgetorpeek(bool advance) KeyStuffed = true; } if (typebuf.tb_no_abbr_cnt == 0) { - typebuf.tb_no_abbr_cnt = 1; // no abbreviations now + typebuf.tb_no_abbr_cnt = 1; // no abbreviations now } } else { - /* - * Loop until we either find a matching mapped key, or we - * are sure that it is not a mapped key. - * If a mapped key sequence is found we go back to the start to - * try re-mapping. - */ - for (;; ) { - /* - * os_breakcheck() is slow, don't use it too often when - * inside a mapping. But call it each time for typed - * characters. - */ + // Loop until we either find a matching mapped key, or we + // are sure that it is not a mapped key. + // If a mapped key sequence is found we go back to the start to + // try re-mapping. + for (;;) { + // os_breakcheck() is slow, don't use it too often when + // inside a mapping. But call it each time for typed + // characters. if (typebuf.tb_maplen) { line_breakcheck(); } else { - os_breakcheck(); // check for CTRL-C + os_breakcheck(); // check for CTRL-C } - keylen = 0; + int keylen = 0; if (got_int) { // flush all input c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L); + // If inchar() returns TRUE (script file was active) or we // are inside a mapping, get out of Insert mode. // Otherwise we behave like having gotten a CTRL-C. @@ -1831,361 +2137,49 @@ static int vgetorpeek(bool advance) break; } else if (typebuf.tb_len > 0) { - /* - * Check for a mappable key sequence. - * Walk through one maphash[] list until we find an - * entry that matches. - * - * Don't look for mappings if: - * - no_mapping set: mapping disabled (e.g. for CTRL-V) - * - maphash_valid not set: no mappings present. - * - typebuf.tb_buf[typebuf.tb_off] should not be remapped - * - in insert or cmdline mode and 'paste' option set - * - waiting for "hit return to continue" and CR or SPACE - * typed - * - waiting for a char with --more-- - * - in Ctrl-X mode, and we get a valid char for that mode - */ - mp = NULL; - max_mlen = 0; - c1 = typebuf.tb_buf[typebuf.tb_off]; - if (no_mapping == 0 && maphash_valid - && (no_zero_mapping == 0 || c1 != '0') - && (typebuf.tb_maplen == 0 - || (p_remap - && (typebuf.tb_noremap[typebuf.tb_off] - & (RM_NONE|RM_ABBR)) == 0)) - && !(p_paste && (State & (INSERT + CMDLINE))) - && !(State == HITRETURN && (c1 == CAR || c1 == ' ')) - && State != ASKMORE - && State != CONFIRM - && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c1)) - || ((compl_cont_status & CONT_LOCAL) - && (c1 == Ctrl_N || - c1 == Ctrl_P)))) { - if (c1 == K_SPECIAL) { - nolmaplen = 2; - } else { - LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0 - && get_real_state() != SELECTMODE); - nolmaplen = 0; - } - // First try buffer-local mappings. - mp = curbuf->b_maphash[MAP_HASH(local_State, c1)]; - mp2 = maphash[MAP_HASH(local_State, c1)]; - if (mp == NULL) { - // There are no buffer-local mappings. - mp = mp2; - mp2 = NULL; - } - /* - * Loop until a partly matching mapping is found or - * all (local) mappings have been checked. - * The longest full match is remembered in "mp_match". - * A full match is only accepted if there is no partly - * match, so "aa" and "aaa" can both be mapped. - */ - mp_match = NULL; - mp_match_len = 0; - for (; mp != NULL; - mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : - (mp = mp->m_next)) { - /* - * Only consider an entry if the first character - * matches and it is for the current state. - * Skip ":lmap" mappings if keys were mapped. - */ - if (mp->m_keys[0] == c1 - && (mp->m_mode & local_State) - && ((mp->m_mode & LANGMAP) == 0 - || typebuf.tb_maplen == 0)) { - int nomap = nolmaplen; - int c2; - // find the match length of this mapping - for (mlen = 1; mlen < typebuf.tb_len; mlen++) { - c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; - if (nomap > 0) { - --nomap; - } else if (c2 == K_SPECIAL) { - nomap = 2; - } else { - LANGMAP_ADJUST(c2, TRUE); - } - if (mp->m_keys[mlen] != c2) { - break; - } - } - - /* Don't allow mapping the first byte(s) of a - * multi-byte char. Happens when mapping - * <M-a> and then changing 'encoding'. Beware - * that 0x80 is escaped. */ - char_u *p1 = mp->m_keys; - char_u *p2 = (char_u *)mb_unescape((const char **)&p1); - - if (p2 != NULL && MB_BYTE2LEN(c1) > utfc_ptr2len(p2)) { - mlen = 0; - } - - // Check an entry whether it matches. - // - Full match: mlen == keylen - // - Partly match: mlen == typebuf.tb_len - keylen = mp->m_keylen; - if (mlen == keylen - || (mlen == typebuf.tb_len - && typebuf.tb_len < keylen)) { - /* - * If only script-local mappings are - * allowed, check if the mapping starts - * with K_SNR. - */ - s = typebuf.tb_noremap + typebuf.tb_off; - if (*s == RM_SCRIPT - && (mp->m_keys[0] != K_SPECIAL - || mp->m_keys[1] != KS_EXTRA - || mp->m_keys[2] - != KE_SNR)) { - continue; - } - /* - * If one of the typed keys cannot be - * remapped, skip the entry. - */ - for (n = mlen; --n >= 0; ) { - if (*s++ & (RM_NONE|RM_ABBR)) { - break; - } - } - if (n >= 0) { - continue; - } - - if (keylen > typebuf.tb_len) { - if (!timedout && !(mp_match != NULL - && mp_match->m_nowait)) { - // break at a partly match - keylen = KEYLEN_PART_MAP; - break; - } - } else if (keylen > mp_match_len - || (keylen == mp_match_len - && mp_match != NULL - && (mp_match->m_mode & LANGMAP) == 0 - && (mp->m_mode & LANGMAP) != 0)) { - // found a longer match - mp_match = mp; - mp_match_len = keylen; - } - } else { - // No match; may have to check for termcode at next character. - if (max_mlen < mlen) { - max_mlen = mlen; - } - } - } - } + // Check for a mapping in "typebuf". + map_result_T result = (map_result_T)handle_mapping(&keylen, &timedout, &mapdepth); - /* If no partly match found, use the longest full - * match. */ - if (keylen != KEYLEN_PART_MAP) { - mp = mp_match; - keylen = mp_match_len; - } - } - - if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { - bool match = typebuf_match_len(p_pt, &mlen); - if (match) { - // write chars to script file(s) - if (mlen > typebuf.tb_maplen) { - gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, - (size_t)(mlen - typebuf.tb_maplen)); - } - - del_typebuf(mlen, 0); // Remove the chars. - set_option_value("paste", !p_paste, NULL, 0); - if (!(State & INSERT)) { - msg_col = 0; - msg_row = Rows - 1; - msg_clr_eos(); // clear ruler - } - status_redraw_all(); - redraw_statuslines(); - showmode(); - setcursor(); - continue; - } - // Need more chars for partly match. - if (mlen == typebuf.tb_len) { - keylen = KEYLEN_PART_KEY; - } else if (max_mlen < mlen) { - // no match, may have to check for termcode at - // next character - max_mlen = mlen + 1; - } + if (result == map_result_retry) { + // try mapping again + continue; } - if ((mp == NULL || max_mlen >= mp_match_len) - && keylen != KEYLEN_PART_MAP) { - // No matching mapping found or found a non-matching mapping that - // matches at least what the matching mapping matched - keylen = 0; - (void)keylen; // suppress clang/dead assignment - // If there was no mapping, use the character from the typeahead - // buffer right here. Otherwise, use the mapping (loop around). - if (mp == NULL) { - // get a character: 2. from the typeahead buffer - c = typebuf.tb_buf[typebuf.tb_off] & 255; - if (advance) { // remove chars from tb_buf - cmd_silent = (typebuf.tb_silent > 0); - if (typebuf.tb_maplen > 0) { - KeyTyped = false; - } else { - KeyTyped = true; - // write char to script file(s) - gotchars(typebuf.tb_buf + typebuf.tb_off, 1); - } - KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]; - del_typebuf(1, 0); - } - break; // got character, break for loop - } else { - keylen = mp_match_len; - } + if (result == map_result_fail) { + // failed, use the outer loop + c = -1; + break; } - // complete match - if (keylen >= 0 && keylen <= typebuf.tb_len) { - int save_m_expr; - int save_m_noremap; - int save_m_silent; - - // Write chars to script file(s) - // Note: :lmap mappings are written *after* being applied. #5658 - if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { - gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, - (size_t)(keylen - typebuf.tb_maplen)); - } - - cmd_silent = (typebuf.tb_silent > 0); - del_typebuf(keylen, 0); // remove the mapped keys - - /* - * Put the replacement string in front of mapstr. - * The depth check catches ":map x y" and ":map y x". - */ - if (++mapdepth >= p_mmd) { - emsg(_("E223: recursive mapping")); - if (State & CMDLINE) { - redrawcmdline(); + if (result == map_result_get) { + // get a character: 2. from the typeahead buffer + c = typebuf.tb_buf[typebuf.tb_off] & 255; + if (advance) { // remove chars from tb_buf + cmd_silent = (typebuf.tb_silent > 0); + if (typebuf.tb_maplen > 0) { + KeyTyped = false; } else { - setcursor(); + KeyTyped = true; + // write char to script file(s) + gotchars(typebuf.tb_buf + typebuf.tb_off, 1); } - flush_buffers(FLUSH_MINIMAL); - mapdepth = 0; // for next one - c = -1; - break; + KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]; + del_typebuf(1, 0); } - - /* - * In Select mode and a Visual mode mapping is used: - * Switch to Visual mode temporarily. Append K_SELECT - * to switch back to Select mode. - */ - if (VIsual_active && VIsual_select - && (mp->m_mode & VISUAL)) { - VIsual_select = false; - (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); - } - - /* Copy the values from *mp that are used, because - * evaluating the expression may invoke a function - * that redefines the mapping, thereby making *mp - * invalid. */ - save_m_expr = mp->m_expr; - save_m_noremap = mp->m_noremap; - save_m_silent = mp->m_silent; - char_u *save_m_keys = NULL; // only saved when needed - char_u *save_m_str = NULL; // only saved when needed - - /* - * Handle ":map <expr>": evaluate the {rhs} as an - * expression. Also save and restore the command line - * for "normal :". - */ - if (mp->m_expr) { - int save_vgetc_busy = vgetc_busy; - const bool save_may_garbage_collect = may_garbage_collect; - - vgetc_busy = 0; - may_garbage_collect = false; - - save_m_keys = vim_strsave(mp->m_keys); - save_m_str = vim_strsave(mp->m_str); - s = eval_map_expr(save_m_str, NUL); - vgetc_busy = save_vgetc_busy; - may_garbage_collect = save_may_garbage_collect; - } else { - s = mp->m_str; - } - - /* - * Insert the 'to' part in the typebuf.tb_buf. - * If 'from' field is the same as the start of the - * 'to' field, don't remap the first character (but do - * allow abbreviations). - * If m_noremap is set, don't remap the whole 'to' - * part. - */ - if (s == NULL) { - i = FAIL; - } else { - int noremap; - - // If this is a LANGMAP mapping, then we didn't record the keys - // at the start of the function and have to record them now. - if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { - gotchars(s, STRLEN(s)); - } - - if (save_m_noremap != REMAP_YES) { - noremap = save_m_noremap; - } else if ( - STRNCMP(s, save_m_keys != NULL - ? save_m_keys : mp->m_keys, - (size_t)keylen) - != 0) { - noremap = REMAP_YES; - } else { - noremap = REMAP_SKIP; - } - i = ins_typebuf(s, noremap, - 0, TRUE, cmd_silent || save_m_silent); - if (save_m_expr) { - xfree(s); - } - } - xfree(save_m_keys); - xfree(save_m_str); - if (i == FAIL) { - c = -1; - break; - } - continue; + break; // got character, break the for loop } + + // not enough characters, get more } - /* - * get a character: 3. from the user - handle <Esc> in Insert mode - */ - /* - * special case: if we get an <ESC> in insert mode and there - * are no more characters at once, we pretend to go out of - * insert mode. This prevents the one second delay after - * typing an <ESC>. If we get something after all, we may - * have to redisplay the mode. That the cursor is in the wrong - * place does not matter. - */ + // get a character: 3. from the user - handle <Esc> in Insert mode + + // special case: if we get an <ESC> in insert mode and there + // are no more characters at once, we pretend to go out of + // insert mode. This prevents the one second delay after + // typing an <ESC>. If we get something after all, we may + // have to redisplay the mode. That the cursor is in the wrong + // place does not matter. c = 0; new_wcol = curwin->w_wcol; new_wrow = curwin->w_wrow; @@ -2196,10 +2190,8 @@ static int vgetorpeek(bool advance) && ex_normal_busy == 0 && typebuf.tb_maplen == 0 && (State & INSERT) - && (p_timeout - || (keylen == KEYLEN_PART_KEY && p_ttimeout)) - && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, - 3, 25L)) == 0) { + && (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout)) + && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) { colnr_T col = 0, vcol; char_u *ptr; @@ -2215,11 +2207,9 @@ static int vgetorpeek(bool advance) if (curwin->w_cursor.col != 0) { if (curwin->w_wcol > 0) { if (did_ai) { - /* - * We are expecting to truncate the trailing - * white-space, so find the last non-white - * character -- webb - */ + // We are expecting to truncate the trailing + // white-space, so find the last non-white + // character -- webb col = vcol = curwin->w_wcol = 0; ptr = get_cursor_line_ptr(); while (col < curwin->w_cursor.col) { @@ -2233,7 +2223,7 @@ static int vgetorpeek(bool advance) + curwin->w_wcol / curwin->w_width_inner; curwin->w_wcol %= curwin->w_width_inner; curwin->w_wcol += curwin_col_off(); - col = 0; // no correction needed + col = 0; // no correction needed } else { --curwin->w_wcol; col = curwin->w_cursor.col - 1; @@ -2261,7 +2251,7 @@ static int vgetorpeek(bool advance) curwin->w_wrow = old_wrow; } if (c < 0) { - continue; // end of input script reached + continue; // end of input script reached } // Allow mapping for just typed characters. When we get here c @@ -2307,9 +2297,8 @@ static int vgetorpeek(bool advance) break; } - /* - * get a character: 3. from the user - update display - */ + // get a character: 3. from the user - update display + // In insert mode a screen update is skipped when characters // are still available. But when those available characters // are part of a mapping, and we are going to do a blocking @@ -2320,26 +2309,21 @@ static int vgetorpeek(bool advance) if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 && advance && must_redraw != 0 && !need_wait_return) { update_screen(0); - setcursor(); // put cursor back where it belongs + setcursor(); // put cursor back where it belongs } - /* - * If we have a partial match (and are going to wait for more - * input from the user), show the partially matched characters - * to the user with showcmd. - */ - i = 0; + // If we have a partial match (and are going to wait for more + // input from the user), show the partially matched characters + // to the user with showcmd. + int showcmd_idx = 0; c1 = 0; if (typebuf.tb_len > 0 && advance && !exmode_active) { - if (((State & (NORMAL | INSERT)) || State == LANGMAP) - && State != HITRETURN) { + if (((State & (NORMAL | INSERT)) || State == LANGMAP) && State != HITRETURN) { // this looks nice when typing a dead character map if (State & INSERT - && ptr2cells(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_len - 1) == 1) { - edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], - false); - setcursor(); // put cursor back where it belongs + && ptr2cells(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) { + edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], false); + setcursor(); // put cursor back where it belongs c1 = 1; } // need to use the col and row from above here @@ -2349,11 +2333,10 @@ static int vgetorpeek(bool advance) curwin->w_wrow = new_wrow; push_showcmd(); if (typebuf.tb_len > SHOWCMD_COLS) { - i = typebuf.tb_len - SHOWCMD_COLS; + showcmd_idx = typebuf.tb_len - SHOWCMD_COLS; } - while (i < typebuf.tb_len) { - (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off - + i++]); + while (showcmd_idx < typebuf.tb_len) { + (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); } curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; @@ -2369,9 +2352,7 @@ static int vgetorpeek(bool advance) } } - /* - * get a character: 3. from the user - get it - */ + // get a character: 3. from the user - get it if (typebuf.tb_len == 0) { // timedout may have been set while waiting for a mapping // that has a <Nop> RHS. @@ -2381,8 +2362,7 @@ static int vgetorpeek(bool advance) long wait_time = 0; if (advance) { - if (typebuf.tb_len == 0 - || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) { + if (typebuf.tb_len == 0 || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) { // blocking wait wait_time = -1L; } else if (keylen == KEYLEN_PART_KEY && p_ttm >= 0) { @@ -2397,7 +2377,7 @@ static int vgetorpeek(bool advance) typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1, wait_time); - if (i != 0) { + if (showcmd_idx != 0) { pop_showcmd(); } if (c1 == 1) { @@ -2407,47 +2387,45 @@ static int vgetorpeek(bool advance) if (State & CMDLINE) { unputcmdline(); } else { - setcursor(); // put cursor back where it belongs + setcursor(); // put cursor back where it belongs } } if (c < 0) { - continue; // end of input script reached + continue; // end of input script reached } - if (c == NUL) { // no character available + if (c == NUL) { // no character available if (!advance) { break; } - if (wait_tb_len > 0) { // timed out + if (wait_tb_len > 0) { // timed out timedout = true; continue; } - } else { // allow mapping for just typed characters + } else { // allow mapping for just typed characters while (typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] != NUL) { typebuf.tb_noremap[typebuf.tb_off + typebuf.tb_len++] = RM_YES; } } - } // for (;;) - } // if (!character from stuffbuf) + } // for (;;) + } // if (!character from stuffbuf) // if advance is false don't loop on NULs } while (c < 0 || (advance && c == NUL)); - /* - * The "INSERT" message is taken care of here: - * if we return an ESC to exit insert mode, the message is deleted - * if we don't return an ESC but deleted the message before, redisplay it - */ + // The "INSERT" message is taken care of here: + // if we return an ESC to exit insert mode, the message is deleted + // if we don't return an ESC but deleted the message before, redisplay it if (advance && p_smd && msg_silent == 0 && (State & INSERT)) { if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) { if (typebuf.tb_len && !KeyTyped) { - redraw_cmdline = true; // delete mode later + redraw_cmdline = true; // delete mode later } else { unshowmode(false); } } else if (c != ESC && mode_deleted) { if (typebuf.tb_len && !KeyTyped) { - redraw_cmdline = true; // show mode later + redraw_cmdline = true; // show mode later } else { showmode(); } @@ -2550,7 +2528,7 @@ int inchar(char_u *buf, int maxlen, long wait_time) #define DUM_LEN MAXMAPLEN * 3 + 3 char_u dum[DUM_LEN + 1]; - for (;; ) { + for (;;) { len = os_inchar(dum, DUM_LEN, 0L, 0, NULL); if (len == 0 || (len == 1 && dum[0] == 3)) { break; @@ -3599,7 +3577,7 @@ char_u *set_context_in_map_cmd(expand_T *xp, char_u *cmd, char_u *arg, bool forc expand_isabbrev = isabbrev; xp->xp_context = EXPAND_MAPPINGS; expand_buffer = false; - for (;; ) { + for (;;) { if (STRNCMP(arg, "<buffer>", 8) == 0) { expand_buffer = true; arg = skipwhite(arg + 8); @@ -3987,7 +3965,7 @@ char_u *vim_strsave_escape_csi(char_u *p) // 0xc0 -> 0xc3 - 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER char_u *res = xmalloc(STRLEN(p) * 4 + 1); char_u *d = res; - for (char_u *s = p; *s != NUL; ) { + for (char_u *s = p; *s != NUL;) { if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) { // Copy special key unmodified. *d++ = *s++; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index e2d3378402..697d4b11a7 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -4,6 +4,7 @@ #include <inttypes.h> #include <stdbool.h> +#include "nvim/ascii.h" #include "nvim/event/loop.h" #include "nvim/ex_eval.h" #include "nvim/iconv.h" @@ -201,7 +202,6 @@ EXTERN bool msg_did_scroll INIT(= false); EXTERN char_u *keep_msg INIT(= NULL); // msg to be shown after redraw EXTERN int keep_msg_attr INIT(= 0); // highlight attr for keep_msg -EXTERN bool keep_msg_more INIT(= false); // keep_msg was set by msgmore() EXTERN bool need_fileinfo INIT(= false); // do fileinfo() after redraw EXTERN int msg_scroll INIT(= false); // msg_start() will scroll EXTERN bool msg_didout INIT(= false); // msg_outstr() was used in line @@ -533,6 +533,11 @@ EXTERN int VIsual_mode INIT(= 'v'); /// true when redoing Visual. EXTERN int redo_VIsual_busy INIT(= false); +// The Visual area is remembered for reselection. +EXTERN int resel_VIsual_mode INIT(= NUL); // 'v', 'V', or Ctrl-V +EXTERN linenr_T resel_VIsual_line_count; // number of lines +EXTERN colnr_T resel_VIsual_vcol; // nr of cols or end col + /// When pasting text with the middle mouse button in visual mode with /// restart_edit set, remember where it started so we can set Insstart. EXTERN pos_T where_paste_started; @@ -627,6 +632,7 @@ EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p. EXTERN int reg_recording INIT(= 0); // register for recording or zero EXTERN int reg_executing INIT(= 0); // register being executed or zero +EXTERN int reg_recorded INIT(= 0); // last recorded register or zero EXTERN int no_mapping INIT(= false); // currently no mapping allowed EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed @@ -727,6 +733,7 @@ EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or // :bufdo is executing EXTERN bool need_start_insertmode INIT(= false); // start insert mode soon +EXTERN char *last_mode INIT(= NULL); EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":) EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "." EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline @@ -789,6 +796,8 @@ extern char_u *compiled_sys; // directory is not a local directory, globaldir is NULL. EXTERN char_u *globaldir INIT(= NULL); +EXTERN char *last_chdir_reason INIT(= NULL); + // Whether 'keymodel' contains "stopsel" and "startsel". EXTERN bool km_stopsel INIT(= false); EXTERN bool km_startsel INIT(= false); @@ -842,6 +851,7 @@ EXTERN disptick_T display_tick INIT(= 0); // cursor position in Insert mode. EXTERN linenr_T spell_redraw_lnum INIT(= 0); +// uncrustify:off // The error messages that can be shared are included here. // Excluded are errors that are only used once and debugging messages. @@ -850,12 +860,8 @@ EXTERN char e_afterinit[] INIT(= N_("E905: Cannot set this option after startup" EXTERN char e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job")); EXTERN char e_argreq[] INIT(= N_("E471: Argument required")); EXTERN char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &")); -EXTERN char e_cmdwin[] INIT(= - N_( - "E11: Invalid in command-line window; <CR> executes, CTRL-C quits")); -EXTERN char e_curdir[] INIT(= - N_( - "E12: Command not allowed from exrc/vimrc in current dir or tag search")); +EXTERN char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits")); +EXTERN char e_curdir[] INIT(= N_( "E12: Command not allowed from exrc/vimrc in current dir or tag search")); EXTERN char e_endif[] INIT(= N_("E171: Missing :endif")); EXTERN char e_endtry[] INIT(= N_("E600: Missing :endtry")); EXTERN char e_endwhile[] INIT(= N_("E170: Missing :endwhile")); @@ -885,10 +891,7 @@ EXTERN char e_channotpty[] INIT(= N_("E904: channel is not a pty")); EXTERN char e_stdiochan2[] INIT(= N_("E905: Couldn't open stdio channel: %s")); EXTERN char e_invstream[] INIT(= N_("E906: invalid stream for channel")); EXTERN char e_invstreamrpc[] INIT(= N_("E906: invalid stream for rpc channel, use 'rpc'")); -EXTERN char e_streamkey[] INIT(= - N_( - "E5210: dict key '%s' already set for buffered stream in channel %" - PRIu64)); +EXTERN char e_streamkey[] INIT(= N_("E5210: dict key '%s' already set for buffered stream in channel %" PRIu64)); EXTERN char e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\"")); EXTERN char e_fsync[] INIT(= N_("E667: Fsync failed: %s")); EXTERN char e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s")); @@ -939,9 +942,7 @@ EXTERN char e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: \"%s\"") EXTERN char e_listreq[] INIT(= N_("E714: List required")); EXTERN char e_listblobreq[] INIT(= N_("E897: List or Blob required")); EXTERN char e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary")); -EXTERN char e_listdictblobarg[] INIT(= - N_( - "E896: Argument of %s must be a List, Dictionary or Blob")); +EXTERN char e_listdictblobarg[] INIT(= N_("E896: Argument of %s must be a List, Dictionary or Blob")); EXTERN char e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); EXTERN char e_sandbox[] INIT(= N_("E48: Not allowed in sandbox")); EXTERN char e_secure[] INIT(= N_("E523: Not allowed here")); @@ -980,25 +981,23 @@ EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long")); EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String")); -EXTERN char e_autocmd_err[] INIT(=N_("E5500: autocmd has thrown an exception: %s")); -EXTERN char e_cmdmap_err[] INIT(=N_("E5520: <Cmd> mapping must end with <CR>")); -EXTERN char -e_cmdmap_repeated[] INIT(=N_("E5521: <Cmd> mapping must end with <CR> before second <Cmd>")); -EXTERN char e_cmdmap_key[] INIT(=N_("E5522: <Cmd> mapping must not include %s key")); +EXTERN char e_autocmd_err[] INIT(= N_("E5500: autocmd has thrown an exception: %s")); +EXTERN char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>")); +EXTERN char e_cmdmap_repeated[] INIT(= N_("E5521: <Cmd> mapping must end with <CR> before second <Cmd>")); +EXTERN char e_cmdmap_key[] INIT(= N_("E5522: <Cmd> mapping must not include %s key")); -EXTERN char e_api_error[] INIT(=N_("E5555: API call: %s")); +EXTERN char e_api_error[] INIT(= N_("E5555: API call: %s")); -EXTERN char e_luv_api_disabled[] INIT(=N_("E5560: %s must not be called in a lua loop callback")); +EXTERN char e_luv_api_disabled[] INIT(= N_("E5560: %s must not be called in a lua loop callback")); -EXTERN char e_floatonly[] INIT(=N_( - "E5601: Cannot close window, only floating window would remain")); -EXTERN char e_floatexchange[] INIT(=N_("E5602: Cannot exchange or rotate float")); +EXTERN char e_floatonly[] INIT(= N_("E5601: Cannot close window, only floating window would remain")); +EXTERN char e_floatexchange[] INIT(= N_("E5602: Cannot exchange or rotate float")); EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string required")); -EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= - N_( - "E1155: Cannot define autocommands for ALL events")); +EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events")); + +EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP")); @@ -1020,6 +1019,8 @@ EXTERN bool embedded_mode INIT(= false); // Do not start a UI nor read/write to stdio (unless embedding). EXTERN bool headless_mode INIT(= false); +// uncrustify:on + /// Used to track the status of external functions. /// Currently only used for iconv(). typedef enum { diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 296c5cbdbf..6fc70144ac 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -461,10 +461,10 @@ static void prt_line_number(prt_settings_T *const psettings, const int page_line // Leave two spaces between the number and the text; depends on // PRINT_NUMBER_WIDTH. - char_u tbuf[20]; - snprintf((char *)tbuf, sizeof(tbuf), "%6ld", (long)lnum); + char tbuf[20]; + snprintf(tbuf, sizeof(tbuf), "%6ld", (long)lnum); for (int i = 0; i < 6; i++) { - (void)mch_print_text_out(&tbuf[i], 1); + (void)mch_print_text_out((char_u *)(&tbuf[i]), 1); } if (psettings->do_syntax) { @@ -571,7 +571,7 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, const // Use a negative line number to indicate printing in the top margin. int page_line = 0 - prt_header_height(); mch_print_start_line(true, page_line); - for (char_u *p = tbuf; *p != NUL; ) { + for (char_u *p = tbuf; *p != NUL;) { const int l = utfc_ptr2len(p); assert(l >= 0); if (mch_print_text_out(p, (size_t)l)) { @@ -669,7 +669,7 @@ void ex_hardcopy(exarg_T *eap) // Syntax highlighting of line numbers. if (prt_use_number() && settings.do_syntax) { - int id = syn_name2id((char_u *)"LineNr"); + int id = syn_name2id("LineNr"); if (id > 0) { id = syn_get_final_id(id); } @@ -986,8 +986,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T #define PRT_PS_DEFAULT_DPI (72) // Default user space resolution #define PRT_PS_DEFAULT_FONTSIZE (10) -#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / \ - sizeof(struct prt_mediasize_S)) +#define PRT_MEDIASIZE_LEN ARRAY_SIZE(prt_mediasize) static struct prt_mediasize_S prt_mediasize[] = { @@ -1254,7 +1253,7 @@ static struct prt_dsc_comment_S prt_dsc_table[] = * Variables for the output PostScript file. */ static FILE *prt_ps_fd; -static int prt_file_error; +static bool prt_file_error; static char_u *prt_ps_file_name = NULL; /* @@ -1310,7 +1309,7 @@ static int prt_collate; /* * Buffers used when generating PostScript output */ -static char_u prt_line_buffer[257]; +static char prt_line_buffer[257]; static garray_T prt_ps_buffer = GA_EMPTY_INIT_VALUE; static int prt_do_conv; @@ -1330,13 +1329,13 @@ static void prt_write_file_raw_len(char_u *buffer, size_t bytes) if (!prt_file_error && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd) != bytes) { emsg(_("E455: Error writing to PostScript output file")); - prt_file_error = TRUE; + prt_file_error = true; } } -static void prt_write_file(char_u *buffer) +static void prt_write_file(char *buffer) { - prt_write_file_len(buffer, STRLEN(buffer)); + prt_write_file_len((char_u *)buffer, STRLEN(buffer)); } static void prt_write_file_len(char_u *buffer, size_t bytes) @@ -1349,7 +1348,7 @@ static void prt_write_file_len(char_u *buffer, size_t bytes) */ static void prt_write_string(char *s) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), "%s", s); + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%s", s); prt_write_file(prt_line_buffer); } @@ -1358,7 +1357,7 @@ static void prt_write_string(char *s) */ static void prt_write_int(int i) { - sprintf((char *)prt_line_buffer, "%d ", i); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%d ", i); prt_write_file(prt_line_buffer); } @@ -1367,7 +1366,7 @@ static void prt_write_int(int i) */ static void prt_write_boolean(int b) { - sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F")); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%s ", (b ? "T" : "F")); prt_write_file(prt_line_buffer); } @@ -1376,14 +1375,14 @@ static void prt_write_boolean(int b) */ static void prt_def_font(char *new_name, char *encoding, int height, char *font) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/_%s /VIM-%s /%s ref\n", new_name, encoding, font); prt_write_file(prt_line_buffer); if (prt_out_mbyte) { - sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n", - new_name, height, 500./prt_ps_courier_font.wx, new_name); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/%s %d %f /_%s sffs\n", + new_name, height, 500./prt_ps_courier_font.wx, new_name); } else { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/%s %d /_%s ffs\n", new_name, height, new_name); } prt_write_file(prt_line_buffer); @@ -1394,10 +1393,10 @@ static void prt_def_font(char *new_name, char *encoding, int height, char *font) */ static void prt_def_cidfont(char *new_name, int height, char *cidfont) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/_%s /%s[/%s] vim_composefont\n", new_name, prt_cmap, cidfont); prt_write_file(prt_line_buffer); - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/%s %d /_%s ffs\n", new_name, height, new_name); prt_write_file(prt_line_buffer); } @@ -1407,7 +1406,7 @@ static void prt_def_cidfont(char *new_name, int height, char *cidfont) */ static void prt_dup_cidfont(char *original_name, char *new_name) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/%s %s d\n", new_name, original_name); prt_write_file(prt_line_buffer); } @@ -1444,7 +1443,7 @@ static void prt_write_real(double val, int prec) prt_real_bits(val, prec, &integer, &fraction); // Emit integer part - snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), "%d", integer); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%d", integer); prt_write_file(prt_line_buffer); // Only emit fraction if necessary if (fraction != 0) { @@ -1454,11 +1453,11 @@ static void prt_write_real(double val, int prec) fraction /= 10; } // Emit fraction left padded with zeros - snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), ".%0*d", + snprintf(prt_line_buffer, sizeof(prt_line_buffer), ".%0*d", prec, fraction); prt_write_file(prt_line_buffer); } - sprintf((char *)prt_line_buffer, " "); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), " "); prt_write_file(prt_line_buffer); } @@ -1467,11 +1466,11 @@ static void prt_write_real(double val, int prec) */ static void prt_def_var(char *name, double value, int prec) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/%s ", name); prt_write_file(prt_line_buffer); prt_write_real(value, prec); - sprintf((char *)prt_line_buffer, "d\n"); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), "d\n"); prt_write_file(prt_line_buffer); } @@ -1569,8 +1568,8 @@ static int prt_find_resource(char *name, struct prt_ps_resource_S *resource) // Look for named resource file in runtimepath STRCPY(buffer, "print"); add_pathsep((char *)buffer); - xstrlcat((char *)buffer, name, MAXPATHL); - xstrlcat((char *)buffer, ".ps", MAXPATHL); + STRLCAT(buffer, name, MAXPATHL); + STRLCAT(buffer, ".ps", MAXPATHL); resource->filename[0] = NUL; retval = (do_in_runtimepath(buffer, 0, prt_resource_name, resource->filename) && resource->filename[0] != NUL); @@ -1833,14 +1832,14 @@ static void prt_dsc_start(void) static void prt_dsc_noarg(char *comment) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%%s\n", comment); prt_write_file(prt_line_buffer); } static void prt_dsc_textline(char *comment, char *text) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%%s: %s\n", comment, text); prt_write_file(prt_line_buffer); } @@ -1848,7 +1847,7 @@ static void prt_dsc_textline(char *comment, char *text) static void prt_dsc_text(char *comment, char *text) { // TODO(vim): - should scan 'text' for any chars needing escaping! - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%%s: (%s)\n", comment, text); prt_write_file(prt_line_buffer); } @@ -1859,12 +1858,12 @@ static void prt_dsc_ints(char *comment, int count, int *ints) { int i; - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%%s:", comment); prt_write_file(prt_line_buffer); for (i = 0; i < count; i++) { - sprintf((char *)prt_line_buffer, " %d", ints[i]); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), " %d", ints[i]); prt_write_file(prt_line_buffer); } @@ -1876,15 +1875,15 @@ static void prt_dsc_resources(const char *comment, const char *type, const char FUNC_ATTR_NONNULL_ARG(2, 3) { if (comment != NULL) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%%s: %s", comment, type); } else { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%+ %s", type); } prt_write_file(prt_line_buffer); - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), " %s\n", string); prt_write_file(prt_line_buffer); } @@ -1910,7 +1909,7 @@ static void prt_dsc_requirements(int duplex, int tumble, int collate, int color, return; } - sprintf((char *)prt_line_buffer, "%%%%Requirements:"); + snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%Requirements:"); prt_write_file(prt_line_buffer); if (duplex) { @@ -1928,7 +1927,7 @@ static void prt_dsc_requirements(int duplex, int tumble, int collate, int color, if (num_copies > 1) { prt_write_string(" numcopies("); // Note: no space wanted so don't use prt_write_int() - snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), "%d", + snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%d", num_copies); prt_write_file(prt_line_buffer); prt_write_string(")"); @@ -1939,7 +1938,7 @@ static void prt_dsc_requirements(int duplex, int tumble, int collate, int color, static void prt_dsc_docmedia(char *paper_name, double width, double height, double weight, char *colour, char *type) { - vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), + vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%DocumentMedia: %s ", paper_name); prt_write_file(prt_line_buffer); prt_write_real(width, 2); @@ -1982,7 +1981,7 @@ void mch_print_cleanup(void) if (prt_ps_fd != NULL) { fclose(prt_ps_fd); prt_ps_fd = NULL; - prt_file_error = FALSE; + prt_file_error = false; } if (prt_ps_file_name != NULL) { XFREE_CLEAR(prt_ps_file_name); @@ -2204,7 +2203,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) // Check encoding and character set are compatible if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0) { emsg(_("E673: Incompatible multi-byte encoding and character set.")); - return FALSE; + return false; } // Add charset name if not empty @@ -2216,7 +2215,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) // Add custom CMap character set name if (*p_pmcs == NUL) { emsg(_("E674: printmbcharset cannot be empty with multi-byte encoding.")); - return FALSE; + return false; } STRLCPY(prt_cmap, p_pmcs, sizeof(prt_cmap) - 2); STRCAT(prt_cmap, "-"); @@ -2232,7 +2231,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) if (!mbfont_opts[OPT_MBFONT_REGULAR].present) { emsg(_("E675: No default font specified for multi-byte printing.")); - return FALSE; + return false; } // Derive CID font names with fallbacks if not defined @@ -2426,12 +2425,12 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) prt_need_bgcol = false; prt_need_underline = false; - prt_file_error = FALSE; + prt_file_error = false; return OK; } -static int prt_add_resource(struct prt_ps_resource_S *resource) +static bool prt_add_resource(struct prt_ps_resource_S *resource) { FILE *fd_resource; char_u resource_buffer[512]; @@ -2440,7 +2439,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) fd_resource = os_fopen((char *)resource->filename, READBIN); if (fd_resource == NULL) { semsg(_("E456: Can't open file \"%s\""), resource->filename); - return FALSE; + return false; } switch (resource->type) { case PRT_RESOURCE_TYPE_PROCSET: @@ -2450,19 +2449,19 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) (char *)resource->title); break; default: - return FALSE; + return false; } prt_dsc_textline("BeginDocument", (char *)resource->filename); - for (;; ) { + for (;;) { bytes_read = fread((char *)resource_buffer, sizeof(char_u), sizeof(resource_buffer), fd_resource); if (ferror(fd_resource)) { semsg(_("E457: Can't read PostScript resource file \"%s\""), resource->filename); fclose(fd_resource); - return FALSE; + return false; } if (bytes_read == 0) { break; @@ -2470,7 +2469,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) prt_write_file_raw_len(resource_buffer, bytes_read); if (prt_file_error) { fclose(fd_resource); - return FALSE; + return false; } } fclose(fd_resource); @@ -2479,10 +2478,10 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) prt_dsc_noarg("EndResource"); - return TRUE; + return true; } -int mch_print_begin(prt_settings_T *psettings) +bool mch_print_begin(prt_settings_T *psettings) { int bbox[4]; double left; @@ -2492,11 +2491,10 @@ int mch_print_begin(prt_settings_T *psettings) struct prt_ps_resource_S res_prolog; struct prt_ps_resource_S res_encoding; char buffer[256]; - char_u *p_encoding; + char *p_encoding; char_u *p; struct prt_ps_resource_S res_cidfont; struct prt_ps_resource_S res_cmap; - int retval = FALSE; /* * PS DSC Header comments - no PS code! @@ -2568,25 +2566,25 @@ int mch_print_begin(prt_settings_T *psettings) // Search for external resources VIM supplies if (!prt_find_resource("prolog", &res_prolog)) { emsg(_("E456: Can't find PostScript resource file \"prolog.ps\"")); - return FALSE; + return false; } if (!prt_open_resource(&res_prolog)) { - return FALSE; + return false; } if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION)) { - return FALSE; + return false; } if (prt_out_mbyte) { // Look for required version of multi-byte printing procset if (!prt_find_resource("cidfont", &res_cidfont)) { emsg(_("E456: Can't find PostScript resource file \"cidfont.ps\"")); - return FALSE; + return false; } if (!prt_open_resource(&res_cidfont)) { - return FALSE; + return false; } if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION)) { - return FALSE; + return false; } } @@ -2595,45 +2593,45 @@ int mch_print_begin(prt_settings_T *psettings) // that cannot be found then default to "latin1". // Note: VIM specific encoding header is always skipped. if (!prt_out_mbyte) { - p_encoding = enc_skip(p_penc); + p_encoding = (char *)enc_skip(p_penc); if (*p_encoding == NUL - || !prt_find_resource((char *)p_encoding, &res_encoding)) { + || !prt_find_resource(p_encoding, &res_encoding)) { // 'printencoding' not set or not supported - find alternate int props; - p_encoding = enc_skip(p_enc); - props = enc_canon_props(p_encoding); + p_encoding = (char *)enc_skip(p_enc); + props = enc_canon_props((char_u *)p_encoding); if (!(props & ENC_8BIT) - || !prt_find_resource((char *)p_encoding, &res_encoding)) { + || !prt_find_resource(p_encoding, &res_encoding)) { // 8-bit 'encoding' is not supported // Use latin1 as default printing encoding - p_encoding = (char_u *)"latin1"; - if (!prt_find_resource((char *)p_encoding, &res_encoding)) { + p_encoding = "latin1"; + if (!prt_find_resource(p_encoding, &res_encoding)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), p_encoding); - return FALSE; + return false; } } } if (!prt_open_resource(&res_encoding)) { - return FALSE; + return false; } // For the moment there are no checks on encoding resource files to // perform } else { - p_encoding = enc_skip(p_penc); + p_encoding = (char *)enc_skip(p_penc); if (*p_encoding == NUL) { - p_encoding = enc_skip(p_enc); + p_encoding = (char *)enc_skip(p_enc); } if (prt_use_courier) { // Include ASCII range encoding vector if (!prt_find_resource(prt_ascii_encoding, &res_encoding)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), prt_ascii_encoding); - return FALSE; + return false; } if (!prt_open_resource(&res_encoding)) { - return FALSE; + return false; } // For the moment there are no checks on encoding resource files to // perform @@ -2641,9 +2639,9 @@ int mch_print_begin(prt_settings_T *psettings) } prt_conv.vc_type = CONV_NONE; - if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) { + if (!(enc_canon_props(p_enc) & enc_canon_props((char_u *)p_encoding) & ENC_8BIT)) { // Set up encoding conversion if required - if (convert_setup(&prt_conv, p_enc, p_encoding) == FAIL) { + if (convert_setup(&prt_conv, p_enc, (char_u *)p_encoding) == FAIL) { semsg(_("E620: Unable to convert to print encoding \"%s\""), p_encoding); return false; @@ -2656,10 +2654,10 @@ int mch_print_begin(prt_settings_T *psettings) if (!prt_find_resource(prt_cmap, &res_cmap)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), prt_cmap); - return FALSE; + return false; } if (!prt_open_resource(&res_cmap)) { - return FALSE; + return false; } } @@ -2737,7 +2735,7 @@ int mch_print_begin(prt_settings_T *psettings) // There will be only one Roman font encoding to be included in the PS // file. if (!prt_add_resource(&res_encoding)) { - return FALSE; + return false; } } @@ -2766,23 +2764,23 @@ int mch_print_begin(prt_settings_T *psettings) // When using Courier for ASCII range when printing multi-byte, need to // pick up ASCII encoding to use with it. if (prt_use_courier) { - p_encoding = (char_u *)prt_ascii_encoding; + p_encoding = prt_ascii_encoding; } prt_dsc_resources("IncludeResource", "font", prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]); - prt_def_font("F0", (char *)p_encoding, (int)prt_line_height, + prt_def_font("F0", p_encoding, (int)prt_line_height, prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]); prt_dsc_resources("IncludeResource", "font", prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]); - prt_def_font("F1", (char *)p_encoding, (int)prt_line_height, + prt_def_font("F1", p_encoding, (int)prt_line_height, prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]); prt_dsc_resources("IncludeResource", "font", prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]); - prt_def_font("F2", (char *)p_encoding, (int)prt_line_height, + prt_def_font("F2", p_encoding, (int)prt_line_height, prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]); prt_dsc_resources("IncludeResource", "font", prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]); - prt_def_font("F3", (char *)p_encoding, (int)prt_line_height, + prt_def_font("F3", p_encoding, (int)prt_line_height, prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]); } if (prt_out_mbyte) { @@ -2847,9 +2845,7 @@ int mch_print_begin(prt_settings_T *psettings) prt_dsc_noarg("EndSetup"); // Fail if any problems writing out to the PS file - retval = !prt_file_error; - - return retval; + return !prt_file_error; } void mch_print_end(prt_settings_T *psettings) @@ -2865,7 +2861,7 @@ void mch_print_end(prt_settings_T *psettings) // Write CTRL-D to close serial communication link if used. // NOTHING MUST BE WRITTEN AFTER THIS! - prt_write_file((char_u *)"\004"); + prt_write_file("\004"); if (!prt_file_error && psettings->outfile == NULL && !got_int && !psettings->user_abort) { diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index d4d53c4126..50a03e0c02 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -65,6 +65,8 @@ typedef enum { HLF_LNA, // LineNrAbove HLF_LNB, // LineNrBelow HLF_CLN, // current line number when 'cursorline' is set + HLF_CLS, // current line sign column + HLF_CLF, // current line fold HLF_R, // return to continue message and yes/no questions HLF_S, // status lines HLF_SNC, // status lines of not-current windows @@ -122,6 +124,8 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_LNA] = "LineNrAbove", [HLF_LNB] = "LineNrBelow", [HLF_CLN] = "CursorLineNr", + [HLF_CLS] = "CursorLineSign", + [HLF_CLF] = "CursorLineFold", [HLF_R] = "Question", [HLF_S] = "StatusLine", [HLF_SNC] = "StatusLineNC", diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 5b5f056164..daef8db267 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -223,9 +223,9 @@ void ex_cstag(exarg_T *eap) switch (p_csto) { case 0: if (cs_check_for_connections()) { - ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); - if (ret == FALSE) { + ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false, + false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); if (msg_col) { msg_putchar('\n'); @@ -249,16 +249,16 @@ void ex_cstag(exarg_T *eap) if (cs_check_for_connections()) { ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, - FALSE, FALSE, *eap->cmdlinep); - if (ret == FALSE) { + false, false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); } } } } else if (cs_check_for_connections()) { - ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); - if (ret == FALSE) { + ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false, + false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); } } @@ -520,7 +520,7 @@ add_err: } -static int cs_check_for_connections(void) +static bool cs_check_for_connections(void) { return cs_cnt_connections() > 0; } @@ -557,7 +557,7 @@ static int cs_cnt_matches(size_t idx) int nlines = 0; char *buf = xmalloc(CSREAD_BUFSIZE); - for (;; ) { + for (;;) { errno = 0; if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp)) { if (errno == EINTR) { @@ -601,7 +601,7 @@ static int cs_cnt_matches(size_t idx) if ((stok = strtok(NULL, (const char *)" ")) == NULL) { continue; } - if (strncmp((const char *)stok, "lines", 5)) { + if (strncmp(stok, "lines", 5)) { continue; } @@ -887,20 +887,20 @@ static int cs_find(exarg_T *eap) { char *opt, *pat; - if (cs_check_for_connections() == FALSE) { + if (cs_check_for_connections() == false) { (void)emsg(_("E567: no cscope connections")); - return FALSE; + return false; } if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL) { cs_usage_msg(Find); - return FALSE; + return false; } pat = opt + strlen(opt) + 1; if (pat >= (char *)eap->arg + eap_arg_len) { cs_usage_msg(Find); - return FALSE; + return false; } /* @@ -919,8 +919,8 @@ static int cs_find(exarg_T *eap) /// Common code for cscope find, shared by cs_find() and ex_cstag(). -static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int use_ll, - char_u *cmdline) +static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, + bool use_ll, char_u *cmdline) { char *cmd; int *nummatches; @@ -966,8 +966,8 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us qfpos++; // next symbol must be + or - if (strchr(CSQF_FLAGS, *qfpos) == NULL) { - (void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1));; - return FALSE; + (void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1)); + return false; } if (*qfpos != '0' @@ -982,7 +982,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us // create the actual command to send to cscope cmd = cs_create_cmd(opt, pat); if (cmd == NULL) { - return FALSE; + return false; } nummatches = xmalloc(sizeof(int) * csinfo_size); @@ -1019,7 +1019,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us (void)semsg(_("E259: no matches found for cscope query %s of %s"), opt, pat); } xfree(nummatches); - return FALSE; + return false; } if (qfpos != NULL && *qfpos != '0') { @@ -1064,7 +1064,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us os_remove((char *)tmp); xfree(tmp); xfree(nummatches); - return TRUE; + return true; } else { char **matches = NULL, **contexts = NULL; size_t matched = 0; @@ -1073,7 +1073,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us cs_fill_results(pat, totmatches, nummatches, &matches, &contexts, &matched); xfree(nummatches); if (matches == NULL) { - return FALSE; + return false; } (void)cs_manage_matches(matches, contexts, matched, Store); @@ -1214,8 +1214,8 @@ static cscmd_T *cs_lookup_cmd(exarg_T *eap) } len = strlen(stok); - for (cmdp = cs_cmds; cmdp->name != NULL; ++cmdp) { - if (strncmp((const char *)(stok), cmdp->name, len) == 0) { + for (cmdp = cs_cmds; cmdp->name != NULL; cmdp++) { + if (strncmp(stok, cmdp->name, len) == 0) { return cmdp; } } @@ -1499,12 +1499,13 @@ static void cs_file_results(FILE *f, int *nummatches_a) continue; } - context = xmalloc(strlen(cntx) + 5); + size_t context_len = strlen(cntx) + 5; + context = xmalloc(context_len); if (strcmp(cntx, "<global>") == 0) { - strcpy(context, "<<global>>"); + xstrlcpy(context, "<<global>>", context_len); } else { - sprintf(context, "<<%s>>", cntx); + snprintf(context, context_len, "<<%s>>", cntx); } if (search == NULL) { diff --git a/src/nvim/indent.c b/src/nvim/indent.c index f49aff6643..8cc5bc2436 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -17,7 +17,6 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/plines.h" diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 3e3e07e9d6..faa9b38cf7 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -8,7 +8,6 @@ #include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/misc1.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/edit.h" diff --git a/src/nvim/input.c b/src/nvim/input.c new file mode 100644 index 0000000000..2f7c5c2c16 --- /dev/null +++ b/src/nvim/input.c @@ -0,0 +1,255 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// input.c: high level functions for prompting the user or input +// like yes/no or number prompts. + +#include <inttypes.h> +#include <stdbool.h> + +#include "nvim/func_attr.h" +#include "nvim/getchar.h" +#include "nvim/mbyte.h" +#include "nvim/memory.h" +#include "nvim/input.h" +#include "nvim/mouse.h" +#include "nvim/os/input.h" +#include "nvim/ui.h" +#include "nvim/vim.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "input.c.generated.h" +#endif + +/// Ask for a reply from the user, 'y' or 'n' +/// +/// No other characters are accepted, the message is repeated until a valid +/// reply is entered or <C-c> is hit. +/// +/// @param[in] str Prompt: question to ask user. Is always followed by +/// " (y/n)?". +/// @param[in] direct Determines what function to use to get user input. If +/// true then ui_inchar() will be used, otherwise vgetc(). +/// I.e. when direct is true then characters are obtained +/// directly from the user without buffers involved. +/// +/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. +int ask_yesno(const char *const str, const bool direct) +{ + const int save_State = State; + + no_wait_return++; + State = CONFIRM; // Mouse behaves like with :confirm. + setmouse(); // Disable mouse in xterm. + no_mapping++; + + int r = ' '; + while (r != 'y' && r != 'n') { + // Same highlighting as for wait_return. + smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str); + if (direct) { + r = get_keystroke(NULL); + } else { + r = plain_vgetc(); + } + if (r == Ctrl_C || r == ESC) { + r = 'n'; + } + msg_putchar(r); // Show what you typed. + ui_flush(); + } + no_wait_return--; + State = save_State; + setmouse(); + no_mapping--; + + return r; +} + +/// Get a key stroke directly from the user. +/// +/// Ignores mouse clicks and scrollbar events, except a click for the left +/// button (used at the more prompt). +/// Doesn't use vgetc(), because it syncs undo and eats mapped characters. +/// Disadvantage: typeahead is ignored. +/// Translates the interrupt character for unix to ESC. +int get_keystroke(MultiQueue *events) +{ + char_u *buf = NULL; + int buflen = 150; + int maxlen; + int len = 0; + int n; + int save_mapped_ctrl_c = mapped_ctrl_c; + int waited = 0; + + mapped_ctrl_c = 0; // mappings are not used here + for (;;) { + // flush output before waiting + ui_flush(); + // Leave some room for check_termcode() to insert a key code into (max + // 5 chars plus NUL). And fix_input_buffer() can triple the number of + // bytes. + maxlen = (buflen - 6 - len) / 3; + if (buf == NULL) { + buf = xmalloc((size_t)buflen); + } else if (maxlen < 10) { + // Need some more space. This might happen when receiving a long + // escape sequence. + buflen += 100; + buf = xrealloc(buf, (size_t)buflen); + maxlen = (buflen - 6 - len) / 3; + } + + // First time: blocking wait. Second time: wait up to 100ms for a + // terminal code to complete. + n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events); + if (n > 0) { + // Replace zero and CSI by a special key code. + n = fix_input_buffer(buf + len, n); + len += n; + waited = 0; + } else if (len > 0) { + waited++; // keep track of the waiting time + } + if (n > 0) { // found a termcode: adjust length + len = n; + } + if (len == 0) { // nothing typed yet + continue; + } + + // Handle modifier and/or special key code. + n = buf[0]; + if (n == K_SPECIAL) { + n = TO_SPECIAL(buf[1], buf[2]); + if (buf[1] == KS_MODIFIER + || n == K_IGNORE + || (is_mouse_key(n) && n != K_LEFTMOUSE)) { + if (buf[1] == KS_MODIFIER) { + mod_mask = buf[2]; + } + len -= 3; + if (len > 0) { + memmove(buf, buf + 3, (size_t)len); + } + continue; + } + break; + } + if (MB_BYTE2LEN(n) > len) { + // more bytes to get. + continue; + } + buf[len >= buflen ? buflen - 1 : len] = NUL; + n = utf_ptr2char(buf); + break; + } + xfree(buf); + + mapped_ctrl_c = save_mapped_ctrl_c; + return n; +} + +/// Get a number from the user. +/// When "mouse_used" is not NULL allow using the mouse. +/// +/// @param colon allow colon to abort +int get_number(int colon, int *mouse_used) +{ + int n = 0; + int c; + int typed = 0; + + if (mouse_used != NULL) { + *mouse_used = false; + } + + // When not printing messages, the user won't know what to type, return a + // zero (as if CR was hit). + if (msg_silent != 0) { + return 0; + } + + no_mapping++; + for (;;) { + ui_cursor_goto(msg_row, msg_col); + c = safe_vgetc(); + if (ascii_isdigit(c)) { + n = n * 10 + c - '0'; + msg_putchar(c); + typed++; + } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) { + if (typed > 0) { + msg_puts("\b \b"); + typed--; + } + n /= 10; + } else if (mouse_used != NULL && c == K_LEFTMOUSE) { + *mouse_used = true; + n = mouse_row + 1; + break; + } else if (n == 0 && c == ':' && colon) { + stuffcharReadbuff(':'); + if (!exmode_active) { + cmdline_row = msg_row; + } + skip_redraw = true; // skip redraw once + do_redraw = false; + break; + } else if (c == Ctrl_C || c == ESC || c == 'q') { + n = 0; + break; + } else if (c == CAR || c == NL) { + break; + } + } + no_mapping--; + return n; +} + +/// Ask the user to enter a number. +/// +/// When "mouse_used" is not NULL allow using the mouse and in that case return +/// the line number. +int prompt_for_number(int *mouse_used) +{ + int i; + int save_cmdline_row; + int save_State; + + // When using ":silent" assume that <CR> was entered. + if (mouse_used != NULL) { + msg_puts(_("Type number and <Enter> or click with the mouse " + "(q or empty cancels): ")); + } else { + msg_puts(_("Type number and <Enter> (q or empty cancels): ")); + } + + // Set the state such that text can be selected/copied/pasted and we still + // get mouse events. + save_cmdline_row = cmdline_row; + cmdline_row = 0; + save_State = State; + State = ASKMORE; // prevents a screen update when using a timer + // May show different mouse shape. + setmouse(); + + i = get_number(true, mouse_used); + if (KeyTyped) { + // don't call wait_return() now + if (msg_row > 0) { + cmdline_row = msg_row - 1; + } + need_wait_return = false; + msg_didany = false; + msg_didout = false; + } else { + cmdline_row = save_cmdline_row; + } + State = save_State; + // May need to restore mouse shape. + setmouse(); + + return i; +} diff --git a/src/nvim/input.h b/src/nvim/input.h new file mode 100644 index 0000000000..7975f21215 --- /dev/null +++ b/src/nvim/input.h @@ -0,0 +1,9 @@ +#ifndef NVIM_INPUT_H +#define NVIM_INPUT_H + +#include "nvim/vim.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "input.h.generated.h" +#endif +#endif // NVIM_INPUT_H diff --git a/src/nvim/lib/kbtree.h b/src/nvim/lib/kbtree.h index 5d9c1095bc..617773a79a 100644 --- a/src/nvim/lib/kbtree.h +++ b/src/nvim/lib/kbtree.h @@ -53,7 +53,7 @@ bool is_internal; \ key_t key[2*T-1]; \ kbnode_##name##_t *ptr[]; \ - } ; \ + }; \ typedef struct { \ kbnode_##name##_t *root; \ int n_keys, n_nodes; \ diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h index e0faf94db9..e81db43038 100644 --- a/src/nvim/lib/khash.h +++ b/src/nvim/lib/khash.h @@ -459,7 +459,7 @@ static kh_inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = (khint_t)*s; if (h) { - for (++s ; *s; ++s) { h = (h << 5) - h + (uint8_t)*s; } + for (++s; *s; ++s) { h = (h << 5) - h + (uint8_t)*s; } } return h; } @@ -666,7 +666,7 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key) } \ } -// More conenient interfaces +// More convenient interfaces /*! @function @abstract Instantiate a hash set containing integer keys diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 9f2372f831..b6792a5a97 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -782,10 +782,10 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) break; } #define ADD_TYPE(type, data_key) \ -case kObjectType##type: { \ - nlua_push_##type(lstate, obj.data.data_key, special); \ - break; \ -} + case kObjectType##type: { \ + nlua_push_##type(lstate, obj.data.data_key, special); \ + break; \ + } ADD_TYPE(Boolean, boolean) ADD_TYPE(Integer, integer) ADD_TYPE(Float, floating) @@ -794,10 +794,10 @@ case kObjectType##type: { \ ADD_TYPE(Dictionary, dictionary) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ -case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj.data.integer, special); \ - break; \ -} + case kObjectType##type: { \ + nlua_push_##type(lstate, (type)obj.data.integer, special); \ + break; \ + } ADD_REMOTE_TYPE(Buffer) ADD_REMOTE_TYPE(Window) ADD_REMOTE_TYPE(Tabpage) diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index b27b1ae7a8..107ff22913 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -31,7 +31,6 @@ #include "nvim/map.h" #include "nvim/memline.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/os/os.h" #include "nvim/screen.h" @@ -405,9 +404,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { const char *code = (char *)&shared_module[0]; - if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/shared.lua") + if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua") || nlua_pcall(lstate, 0, 0)) { - nlua_error(lstate, _("E5106: Error while creating shared module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); return 1; } } @@ -417,18 +416,18 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_getfield(lstate, -1, "loaded"); // [package, loaded] const char *code = (char *)&inspect_module[0]; - if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/inspect.lua") + if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua") || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n")); return 1; } // [package, loaded, inspect] lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] code = (char *)&lua_F_module[0]; - if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/F.lua") + if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua") || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n")); return 1; } // [package, loaded, module] @@ -439,9 +438,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { const char *code = (char *)&vim_module[0]; - if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua") + if (luaL_loadbuffer(lstate, code, sizeof(vim_module) - 1, "@vim.lua") || nlua_pcall(lstate, 0, 0)) { - nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n")); return 1; } } @@ -451,9 +450,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_getfield(lstate, -1, "loaded"); // [package, loaded] const char *code = (char *)&lua_meta_module[0]; - if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/_meta.lua") + if (luaL_loadbuffer(lstate, code, sizeof(lua_meta_module) - 1, "@vim/_meta.lua") || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s\n")); return 1; } // [package, loaded, module] @@ -521,6 +520,9 @@ static void nlua_print_event(void **argv) const size_t len = (size_t)(intptr_t)argv[1]-1; // exclude final NUL for (size_t i = 0; i < len;) { + if (got_int) { + break; + } const size_t start = i; while (i < len) { switch (str[i]) { @@ -957,7 +959,7 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) while ((line = fgetline(0, cookie, 0, false)) != NULL) { GA_APPEND(char_u *, &ga, line); } - char *code = (char *)ga_concat_strings_sep(&ga, "\n"); + char *code = ga_concat_strings_sep(&ga, "\n"); size_t len = strlen(code); nlua_typval_exec(code, len, name, NULL, 0, false, NULL); diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index db79e9e7e9..0d6789317c 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -34,7 +34,6 @@ #include "nvim/map.h" #include "nvim/memline.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/os/os.h" #include "nvim/regexp.h" @@ -175,13 +174,13 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); intptr_t idx; - if (lua_gettop(lstate) >= 2) { + if (lua_isnoneornil(lstate, 2)) { + idx = (intptr_t)s1_len; + } else { idx = luaL_checkinteger(lstate, 2); if (idx < 0 || idx > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - } else { - idx = (intptr_t)s1_len; } size_t codepoints = 0, codeunits = 0; @@ -231,8 +230,8 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int tail_offset = mb_head_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1); - lua_pushinteger(lstate, tail_offset); + int head_offset = mb_head_off((char_u *)s1, (char_u *)s1 + offset - 1); + lua_pushinteger(lstate, head_offset); return 1; } @@ -251,7 +250,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1); + int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + offset - 1); lua_pushinteger(lstate, tail_offset); return 1; } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 30c7034209..c1a1e7f162 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -323,6 +323,7 @@ end do local validate = vim.validate + --@private local function make_dict_accessor(scope, handle) validate { scope = {scope, 's'}; @@ -422,11 +423,10 @@ end --- --- Without a runtime, writes to :Messages ---@see :help nvim_notify ----@param msg Content of the notification to show to the user ----@param log_level Optional log level ----@param opts Dictionary with optional options (timeout, etc) -function vim.notify(msg, log_level, _opts) - +---@param msg string Content of the notification to show to the user +---@param log_level number|nil enum from vim.log.levels +---@param opts table|nil additional options (timeout, etc) +function vim.notify(msg, log_level, opts) -- luacheck: no unused if log_level == vim.log.levels.ERROR then vim.api.nvim_err_writeln(msg) elseif log_level == vim.log.levels.WARN then @@ -454,7 +454,7 @@ local on_key_cbs = {} --- On each key press, Nvim passes the key char to fn(). |i_CTRL-V| --- If {fn} is nil, it removes the callback for the associated {ns_id} ---@param ns_id number? Namespace ID. If nil or 0, generates and returns a new ---- |nvim_create_namesapce()| id. +--- |nvim_create_namespace()| id. --- ---@return number Namespace id associated with {fn}. Or count of all callbacks ---if on_key() is called without arguments. @@ -580,6 +580,7 @@ function vim._expand_pat(pat, env) end local keys = {} + ---@private local function insert_keys(obj) for k,_ in pairs(obj) do if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index 7eda9b6270..b2e971f9f3 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -17,7 +17,7 @@ #include "xdiff/xdiff.h" typedef enum { - kNluaXdiffModeUnified = 0, + kNluaXdiffModeUnified = 0, kNluaXdiffModeOnHunkCB, kNluaXdiffModeLocations, } NluaXdiffMode; diff --git a/src/nvim/macros.h b/src/nvim/macros.h index faef0f69b2..c2b2c89abf 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -54,7 +54,7 @@ #define ASCII_ISALNUM(c) (ASCII_ISALPHA(c) || ascii_isdigit(c)) // Returns empty string if it is NULL. -#define EMPTY_IF_NULL(x) ((x) ? (x) : (char_u *)"") +#define EMPTY_IF_NULL(x) (char *)((x) ? (x) : (char_u *)"") // Adjust chars in a language according to 'langmap' option. // NOTE that there is no noticeable overhead if 'langmap' is not set. diff --git a/src/nvim/main.c b/src/nvim/main.c index 921bc883cf..cbd1f53727 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -39,7 +39,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -696,6 +695,50 @@ void getout(int exitval) os_exit(exitval); } +/// Preserve files and exit. +/// @note IObuff must contain a message. +/// @note This may be called from deadly_signal() in a signal handler, avoid +/// unsafe functions, such as allocating memory. +void preserve_exit(void) + FUNC_ATTR_NORETURN +{ + // 'true' when we are sure to exit, e.g., after a deadly signal + static bool really_exiting = false; + + // Prevent repeated calls into this method. + if (really_exiting) { + if (input_global_fd() >= 0) { + // normalize stream (#2598) + stream_set_blocking(input_global_fd(), true); + } + exit(2); + } + + really_exiting = true; + // Ignore SIGHUP while we are already exiting. #9274 + signal_reject_deadly(); + mch_errmsg(IObuff); + mch_errmsg("\n"); + ui_flush(); + + ml_close_notmod(); // close all not-modified buffers + + FOR_ALL_BUFFERS(buf) { + if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) { + mch_errmsg("Vim: preserving files...\r\n"); + ui_flush(); + ml_sync_all(false, false, true); // preserve all swap files + break; + } + } + + ml_close_all(false); // close all memfiles, without deleting + + mch_errmsg("Vim: Finished.\r\n"); + + getout(1); +} + /// Gets the integer value of a numeric command line argument if given, /// such as '-o10'. /// diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 51f4ba635f..39f18b333d 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -235,7 +235,7 @@ pos_T *movemark(int count) return (pos_T *)NULL; } - for (;; ) { + for (;;) { if (curwin->w_jumplistidx + count < 0 || curwin->w_jumplistidx + count >= curwin->w_jumplistlen) { return (pos_T *)NULL; diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 12460646ed..5eb209a6f6 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -42,13 +42,13 @@ #include "nvim/eval.h" #include "nvim/fileio.h" #include "nvim/func_attr.h" +#include "nvim/getchar.h" #include "nvim/iconv.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/path.h" @@ -764,7 +764,7 @@ int utfc_ptr2char(const char_u *p, int *pcc) && p[len] >= 0x80 && utf_composinglike(p, p + len)) { cc = utf_ptr2char(p + len); - for (;; ) { + for (;;) { pcc[i++] = cc; if (i == MAX_MCO) { break; @@ -1322,7 +1322,7 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2 int c1, c2, cdiff; char_u buffer[6]; - for (;; ) { + for (;;) { c1 = utf_safe_read_char_adv(&s1, &n1); c2 = utf_safe_read_char_adv(&s2, &n2); @@ -1609,7 +1609,8 @@ void show_utf8(void) msg((char *)IObuff); } -/// Return offset from "p" to the first byte of the character it points into. +/// Return offset from "p" to the start of a character, including composing characters. +/// "base" must be the start of the string, which must be NUL terminated. /// If "p" points to the NUL at the end of the string return 0. /// Returns 0 when already at the first byte of a character. int utf_head_off(const char_u *base, const char_u *p) @@ -1850,10 +1851,9 @@ int mb_off_next(char_u *base, char_u *p) return i; } -/* - * Return the offset from "p" to the last byte of the character it points - * into. Can start anywhere in a stream of bytes. - */ +/// Return the offset from "p" to the last byte of the character it points +/// into. Can start anywhere in a stream of bytes. +/// Composing characters are not included. int mb_tail_off(char_u *base, char_u *p) { int i; @@ -1933,7 +1933,7 @@ void utf_find_illegal(void) } curwin->w_cursor.coladd = 0; - for (;; ) { + for (;;) { p = get_cursor_pos_ptr(); if (vimconv.vc_type != CONV_NONE) { xfree(tofree); @@ -2363,7 +2363,7 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen from = (char *)str; fromlen = slen; - for (;; ) { + for (;;) { if (len == 0 || ICONV_ERRNO == ICONV_E2BIG) { // Allocate enough room for most conversions. When re-allocating // increase the buffer size. diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 2a0ea9a269..3397296b3a 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -505,7 +505,7 @@ bool mf_release_all(void) // Flush as many blocks as possible, only if there is a swapfile. if (mfp->mf_fd >= 0) { - for (bhdr_T *hp = mfp->mf_used_last; hp != NULL; ) { + for (bhdr_T *hp = mfp->mf_used_last; hp != NULL;) { if (!(hp->bh_flags & BH_LOCKED) && (!(hp->bh_flags & BH_DIRTY) || mf_write(mfp, hp) != FAIL)) { diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 456b1013c1..08521c0dc3 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -51,6 +51,7 @@ #include "nvim/fileio.h" #include "nvim/func_attr.h" #include "nvim/getchar.h" +#include "nvim/input.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -58,7 +59,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" @@ -407,7 +407,7 @@ void ml_setname(buf_T *buf) */ dirp = p_dir; bool found_existing_dir = false; - for (;; ) { + for (;;) { if (*dirp == NUL) { // tried all directories, fail break; } @@ -504,7 +504,7 @@ void ml_open_file(buf_T *buf) */ dirp = p_dir; bool found_existing_dir = false; - for (;; ) { + for (;;) { if (*dirp == NUL) { break; } @@ -1236,7 +1236,7 @@ void ml_recover(bool checkext) } else if (error) { ++no_wait_return; msg(">>>>>>>>>>>>>"); - emsg(_( "E312: Errors detected while recovering; look for lines starting with ???")); + emsg(_("E312: Errors detected while recovering; look for lines starting with ???")); --no_wait_return; msg(_("See \":help E312\" for more information.")); msg(">>>>>>>>>>>>>"); @@ -1841,6 +1841,17 @@ char_u *ml_get_pos(const pos_T *pos) return ml_get_buf(curbuf, pos->lnum, false) + pos->col; } +/// get codepoint at pos. pos must be either valid or have col set to MAXCOL! +int gchar_pos(pos_T *pos) + FUNC_ATTR_NONNULL_ARG(1) +{ + // When searching columns is sometimes put at the end of a line. + if (pos->col == MAXCOL) { + return NUL; + } + return utf_ptr2char(ml_get_pos(pos)); +} + /// Return a pointer to a line in a specific buffer /// /// @param will_change true mark the buffer dirty (chars in the line will be changed) @@ -2349,7 +2360,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * move some of the pointer into the new block * prepare for updating the parent block */ - for (;; ) { // do this twice when splitting block 1 + for (;;) { // do this twice when splitting block 1 hp_new = ml_new_ptr(mfp); if (hp_new == NULL) { // TODO: try to fix tree return FAIL; @@ -2564,7 +2575,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) */ if (buf->b_ml.ml_line_count == 1) { // file becomes empty if (message) { - set_keep_msg((char_u *)_(no_lines_msg), 0); + set_keep_msg(_(no_lines_msg), 0); } i = ml_replace((linenr_T)1, (char_u *)"", true); @@ -2733,7 +2744,7 @@ linenr_T ml_firstmarked(void) * The search starts with lowest_marked line. This is the last line where * a mark was found, adjusted by inserting/deleting lines. */ - for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; ) { + for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count;) { /* * Find the data block containing the line. * This also fills the stack with the blocks from the root to the data @@ -2775,7 +2786,7 @@ void ml_clearmarked(void) /* * The search starts with line lowest_marked. */ - for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; ) { + for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count;) { /* * Find the data block containing the line. * This also fills the stack with the blocks from the root to the data @@ -3034,7 +3045,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) /* * search downwards in the tree until a data block is found */ - for (;; ) { + for (;;) { if ((hp = mf_get(mfp, bnum, page_count)) == NULL) { goto error_noblock; } @@ -3211,7 +3222,7 @@ int resolve_symlink(const char_u *fname, char_u *buf) // Put the result so far in tmp[], starting with the original name. STRLCPY(tmp, fname, MAXPATHL); - for (;; ) { + for (;;) { // Limit symlink depth to 100, catch recursive loops. if (++depth == 100) { semsg(_("E773: Symlink loop for \"%s\""), fname); @@ -3481,7 +3492,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ fname = (char *)makeswapname((char_u *)buf_fname, buf->b_ffname, buf, (char_u *)dir_name); - for (;; ) { + for (;;) { if (fname == NULL) { // must be out of memory break; } diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 547a9015b7..677ff8f522 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -17,7 +17,6 @@ #include "nvim/memfile.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/sign.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -573,7 +572,6 @@ void free_all_mem(void) return; } entered_free_all_mem = true; - // Don't want to trigger autocommands from here on. block_autocmds(); @@ -632,6 +630,7 @@ void free_all_mem(void) clear_sb_text(true); // free any scrollback text // Free some global vars. + xfree(last_mode); xfree(last_cmdline); xfree(new_last_cmdline); set_keep_msg(NULL, 0); @@ -672,7 +671,7 @@ void free_all_mem(void) first_tabpage = NULL; // message history - for (;; ) { + for (;;) { if (delete_first_msg() == FAIL) { break; } @@ -687,7 +686,7 @@ void free_all_mem(void) // Must be after eval_clear to avoid it trying to access b:changedtick after // freeing it. p_acd = false; - for (buf = firstbuf; buf != NULL; ) { + for (buf = firstbuf; buf != NULL;) { bufref_T bufref; set_bufref(&bufref, buf); nextbuf = buf->b_next; diff --git a/src/nvim/menu.c b/src/nvim/menu.c index c2b6a5e402..ac4d52c392 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -22,7 +22,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/screen.h" #include "nvim/state.h" #include "nvim/strings.h" @@ -80,7 +79,7 @@ void ex_menu(exarg_T *eap) modes = get_menu_cmd_modes((char *)eap->cmd, eap->forceit, &noremap, &unmenu); arg = eap->arg; - for (;; ) { + for (;;) { if (STRNCMP(arg, "<script>", 8) == 0) { noremap = REMAP_SCRIPT; arg = skipwhite(arg + 8); @@ -449,7 +448,7 @@ static int add_menu_path(const char_u *const menu_path, vimmenu_T *menuarg, } if (c != 0) { - menu->strings[i] = xmalloc(STRLEN(call_data) + 5 ); + menu->strings[i] = xmalloc(STRLEN(call_data) + 5); menu->strings[i][0] = c; if (d == 0) { STRCPY(menu->strings[i] + 1, call_data); @@ -1315,7 +1314,7 @@ static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext) } // Find mnemonic characters "&a" and reduce "&&" to "&". - for (p = text; p != NULL; ) { + for (p = text; p != NULL;) { p = vim_strchr(p, '&'); if (p != NULL) { if (p[1] == NUL) { // trailing "&" diff --git a/src/nvim/message.c b/src/nvim/message.c index 7b6337bea2..befca8c76b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -23,12 +23,12 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/input.h" #include "nvim/keymap.h" #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -76,6 +76,8 @@ static int msg_hist_len = 0; static FILE *verbose_fd = NULL; static int verbose_did_open = FALSE; +bool keep_msg_more = false; // keep_msg was set by msgmore() + /* * When writing messages to the screen, there are many different situations. * A number of variables is used to remember the current state: @@ -329,7 +331,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) if (keep && retval && vim_strsize((char_u *)s) < (int)(Rows - cmdline_row - 1) * Columns + sc_col) { - set_keep_msg((char_u *)s, 0); + set_keep_msg((char *)s, 0); } xfree(buf); @@ -401,7 +403,7 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen) } len += n; buf[e] = s[e]; - for (n = utfc_ptr2len(s + e); --n > 0; ) { + for (n = utfc_ptr2len(s + e); --n > 0;) { if (++e == buflen) { break; } @@ -732,7 +734,7 @@ static bool emsg_multiline(const char *s, bool multiline) /// @return true if wait_return not called bool emsg(const char *s) { - return emsg_multiline((const char *)s, false); + return emsg_multiline(s, false); } void emsg_invreg(int name) @@ -839,13 +841,11 @@ void msg_schedule_semsg(const char *const fmt, ...) multiqueue_put(main_loop.events, msg_semsg_event, 1, s); } -/* - * Like msg(), but truncate to a single line if p_shm contains 't', or when - * "force" is TRUE. This truncates in another way as for normal messages. - * Careful: The string may be changed by msg_may_trunc()! - * Returns a pointer to the printed message, if wait_return() not called. - */ -char *msg_trunc_attr(char *s, int force, int attr) +// Like msg(), but truncate to a single line if p_shm contains 't', or when +// "force" is true. This truncates in another way as for normal messages. +// Careful: The string may be changed by msg_may_trunc()! +// Returns a pointer to the printed message, if wait_return() not called. +char *msg_trunc_attr(char *s, bool force, int attr) { int n; @@ -869,7 +869,7 @@ char *msg_trunc_attr(char *s, int force, int attr) * Return a pointer to where the truncated message starts. * Note: May change the message by replacing a character with '<'. */ -char_u *msg_may_trunc(int force, char_u *s) +char_u *msg_may_trunc(bool force, char_u *s) { int room; @@ -883,7 +883,7 @@ char_u *msg_may_trunc(int force, char_u *s) return s; } int n; - for (n = 0; size >= room; ) { + for (n = 0; size >= room;) { size -= utf_ptr2cells(s + n); n += utfc_ptr2len(s + n); } @@ -1288,11 +1288,11 @@ static void hit_return_msg(void) /* * Set "keep_msg" to "s". Free the old value and check for NULL pointer. */ -void set_keep_msg(char_u *s, int attr) +void set_keep_msg(char *s, int attr) { xfree(keep_msg); if (s != NULL && msg_silent == 0) { - keep_msg = vim_strsave(s); + keep_msg = vim_strsave((char_u *)s); } else { keep_msg = NULL; } @@ -1300,6 +1300,49 @@ void set_keep_msg(char_u *s, int attr) keep_msg_attr = attr; } +void msgmore(long n) +{ + long pn; + + if (global_busy // no messages now, wait until global is finished + || !messaging()) { // 'lazyredraw' set, don't do messages now + return; + } + + // We don't want to overwrite another important message, but do overwrite + // a previous "more lines" or "fewer lines" message, so that "5dd" and + // then "put" reports the last action. + if (keep_msg != NULL && !keep_msg_more) { + return; + } + + if (n > 0) { + pn = n; + } else { + pn = -n; + } + + if (pn > p_report) { + if (n > 0) { + vim_snprintf(msg_buf, MSG_BUF_LEN, + NGETTEXT("%ld more line", "%ld more lines", pn), + pn); + } else { + vim_snprintf(msg_buf, MSG_BUF_LEN, + NGETTEXT("%ld line less", "%ld fewer lines", pn), + pn); + } + if (got_int) { + xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN); + } + if (msg(msg_buf)) { + set_keep_msg(msg_buf, 0); + keep_msg_more = true; + } + } +} + + void msg_ext_set_kind(const char *msg_kind) { // Don't change the label of an existing batch: @@ -2056,7 +2099,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs msg_ext_last_attr = attr; } // Concat pieces with the same highlight - size_t len = strnlen((char *)str, maxlen); // -V781 + size_t len = STRNLEN(str, maxlen); // -V781 ga_concat_len(&msg_ext_last_chunk, (char *)str, len); msg_ext_cur_len += len; return; @@ -2549,7 +2592,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) msgchunk_T *mp = smp; char_u *p; - for (;; ) { + for (;;) { msg_row = row; msg_col = mp->sb_msg_col; p = mp->sb_text; @@ -2688,7 +2731,7 @@ static int do_more_prompt(int typed_char) if (typed_char == NUL) { msg_moremsg(FALSE); } - for (;; ) { + for (;;) { /* * Get a typed character directly from the user. */ @@ -3342,7 +3385,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) } if (msg_attr((const char *)message, keep_msg_attr) && msg_scrolled == 0) { - set_keep_msg(message, keep_msg_attr); + set_keep_msg((char *)message, keep_msg_attr); } msg_didout = false; // Overwrite this message. msg_nowait = true; // Don't wait for this message. @@ -3436,7 +3479,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl ++no_wait_return; hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); - for (;; ) { + for (;;) { // Get a typed character directly from the user. c = get_keystroke(NULL); switch (c) { diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c deleted file mode 100644 index faf0b0f633..0000000000 --- a/src/nvim/misc1.c +++ /dev/null @@ -1,1061 +0,0 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - -/* - * misc1.c: functions that didn't seem to fit elsewhere - */ - -#include <assert.h> -#include <inttypes.h> -#include <limits.h> -#include <stdbool.h> -#include <string.h> - -#include "nvim/ascii.h" -#include "nvim/buffer.h" -#include "nvim/buffer_updates.h" -#include "nvim/charset.h" -#include "nvim/cursor.h" -#include "nvim/diff.h" -#include "nvim/edit.h" -#include "nvim/eval.h" -#include "nvim/event/stream.h" -#include "nvim/ex_cmds.h" -#include "nvim/ex_docmd.h" -#include "nvim/ex_getln.h" -#include "nvim/fileio.h" -#include "nvim/fold.h" -#include "nvim/func_attr.h" -#include "nvim/garray.h" -#include "nvim/getchar.h" -#include "nvim/indent.h" -#include "nvim/indent_c.h" -#include "nvim/main.h" -#include "nvim/mbyte.h" -#include "nvim/memline.h" -#include "nvim/memory.h" -#include "nvim/message.h" -#include "nvim/misc1.h" -#include "nvim/mouse.h" -#include "nvim/move.h" -#include "nvim/option.h" -#include "nvim/os/input.h" -#include "nvim/os/os.h" -#include "nvim/os/shell.h" -#include "nvim/os/signal.h" -#include "nvim/os/time.h" -#include "nvim/os_unix.h" -#include "nvim/quickfix.h" -#include "nvim/regexp.h" -#include "nvim/screen.h" -#include "nvim/search.h" -#include "nvim/state.h" -#include "nvim/strings.h" -#include "nvim/tag.h" -#include "nvim/ui.h" -#include "nvim/undo.h" -#include "nvim/vim.h" -#include "nvim/window.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "misc1.c.generated.h" -#endif -// All user names (for ~user completion as done by shell). -static garray_T ga_users = GA_EMPTY_INIT_VALUE; - -/* - * get_leader_len() returns the length in bytes of the prefix of the given - * string which introduces a comment. If this string is not a comment then - * 0 is returned. - * When "flags" is not NULL, it is set to point to the flags of the recognized - * comment leader. - * "backward" must be true for the "O" command. - * If "include_space" is set, include trailing whitespace while calculating the - * length. - */ -int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space) -{ - int i, j; - int result; - int got_com = FALSE; - int found_one; - char_u part_buf[COM_MAX_LEN]; // buffer for one option part - char_u *string; // pointer to comment string - char_u *list; - int middle_match_len = 0; - char_u *prev_list; - char_u *saved_flags = NULL; - - result = i = 0; - while (ascii_iswhite(line[i])) { // leading white space is ignored - ++i; - } - - /* - * Repeat to match several nested comment strings. - */ - while (line[i] != NUL) { - /* - * scan through the 'comments' option for a match - */ - found_one = FALSE; - for (list = curbuf->b_p_com; *list; ) { - // Get one option part into part_buf[]. Advance "list" to next - // one. Put "string" at start of string. - if (!got_com && flags != NULL) { - *flags = list; // remember where flags started - } - prev_list = list; - (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); - string = vim_strchr(part_buf, ':'); - if (string == NULL) { // missing ':', ignore this part - continue; - } - *string++ = NUL; // isolate flags from string - - // If we found a middle match previously, use that match when this - // is not a middle or end. - if (middle_match_len != 0 - && vim_strchr(part_buf, COM_MIDDLE) == NULL - && vim_strchr(part_buf, COM_END) == NULL) { - break; - } - - // When we already found a nested comment, only accept further - // nested comments. - if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) { - continue; - } - - // When 'O' flag present and using "O" command skip this one. - if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) { - continue; - } - - // Line contents and string must match. - // When string starts with white space, must have some white space - // (but the amount does not need to match, there might be a mix of - // TABs and spaces). - if (ascii_iswhite(string[0])) { - if (i == 0 || !ascii_iswhite(line[i - 1])) { - continue; // missing white space - } - while (ascii_iswhite(string[0])) { - ++string; - } - } - for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) { - } - if (string[j] != NUL) { - continue; // string doesn't match - } - // When 'b' flag used, there must be white space or an - // end-of-line after the string in the line. - if (vim_strchr(part_buf, COM_BLANK) != NULL - && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { - continue; - } - - // We have found a match, stop searching unless this is a middle - // comment. The middle comment can be a substring of the end - // comment in which case it's better to return the length of the - // end comment and its flags. Thus we keep searching with middle - // and end matches and use an end match if it matches better. - if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { - if (middle_match_len == 0) { - middle_match_len = j; - saved_flags = prev_list; - } - continue; - } - if (middle_match_len != 0 && j > middle_match_len) { - // Use this match instead of the middle match, since it's a - // longer thus better match. - middle_match_len = 0; - } - - if (middle_match_len == 0) { - i += j; - } - found_one = TRUE; - break; - } - - if (middle_match_len != 0) { - // Use the previously found middle match after failing to find a - // match with an end. - if (!got_com && flags != NULL) { - *flags = saved_flags; - } - i += middle_match_len; - found_one = TRUE; - } - - // No match found, stop scanning. - if (!found_one) { - break; - } - - result = i; - - // Include any trailing white space. - while (ascii_iswhite(line[i])) { - ++i; - } - - if (include_space) { - result = i; - } - - // If this comment doesn't nest, stop here. - got_com = TRUE; - if (vim_strchr(part_buf, COM_NEST) == NULL) { - break; - } - } - return result; -} - -/* - * Return the offset at which the last comment in line starts. If there is no - * comment in the whole line, -1 is returned. - * - * When "flags" is not null, it is set to point to the flags describing the - * recognized comment leader. - */ -int get_last_leader_offset(char_u *line, char_u **flags) -{ - int result = -1; - int i, j; - int lower_check_bound = 0; - char_u *string; - char_u *com_leader; - char_u *com_flags; - char_u *list; - int found_one; - char_u part_buf[COM_MAX_LEN]; // buffer for one option part - - /* - * Repeat to match several nested comment strings. - */ - i = (int)STRLEN(line); - while (--i >= lower_check_bound) { - /* - * scan through the 'comments' option for a match - */ - found_one = FALSE; - for (list = curbuf->b_p_com; *list; ) { - char_u *flags_save = list; - - /* - * Get one option part into part_buf[]. Advance list to next one. - * put string at start of string. - */ - (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); - string = vim_strchr(part_buf, ':'); - if (string == NULL) { // If everything is fine, this cannot actually - // happen. - continue; - } - *string++ = NUL; // Isolate flags from string. - com_leader = string; - - /* - * Line contents and string must match. - * When string starts with white space, must have some white space - * (but the amount does not need to match, there might be a mix of - * TABs and spaces). - */ - if (ascii_iswhite(string[0])) { - if (i == 0 || !ascii_iswhite(line[i - 1])) { - continue; - } - while (ascii_iswhite(*string)) { - string++; - } - } - for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) { - // do nothing - } - if (string[j] != NUL) { - continue; - } - - /* - * When 'b' flag used, there must be white space or an - * end-of-line after the string in the line. - */ - if (vim_strchr(part_buf, COM_BLANK) != NULL - && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { - continue; - } - - if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { - // For a middlepart comment, only consider it to match if - // everything before the current position in the line is - // whitespace. Otherwise we would think we are inside a - // comment if the middle part appears somewhere in the middle - // of the line. E.g. for C the "*" appears often. - for (j = 0; j <= i && ascii_iswhite(line[j]); j++) { - } - if (j < i) { - continue; - } - } - - /* - * We have found a match, stop searching. - */ - found_one = TRUE; - - if (flags) { - *flags = flags_save; - } - com_flags = flags_save; - - break; - } - - if (found_one) { - char_u part_buf2[COM_MAX_LEN]; // buffer for one option part - int len1, len2, off; - - result = i; - /* - * If this comment nests, continue searching. - */ - if (vim_strchr(part_buf, COM_NEST) != NULL) { - continue; - } - - lower_check_bound = i; - - // Let's verify whether the comment leader found is a substring - // of other comment leaders. If it is, let's adjust the - // lower_check_bound so that we make sure that we have determined - // the comment leader correctly. - - while (ascii_iswhite(*com_leader)) { - ++com_leader; - } - len1 = (int)STRLEN(com_leader); - - for (list = curbuf->b_p_com; *list; ) { - char_u *flags_save = list; - - (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ","); - if (flags_save == com_flags) { - continue; - } - string = vim_strchr(part_buf2, ':'); - ++string; - while (ascii_iswhite(*string)) { - ++string; - } - len2 = (int)STRLEN(string); - if (len2 == 0) { - continue; - } - - // Now we have to verify whether string ends with a substring - // beginning the com_leader. - for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2; ) { - --off; - if (!STRNCMP(string + off, com_leader, len2 - off)) { - if (i - off < lower_check_bound) { - lower_check_bound = i - off; - } - } - } - } - } - } - return result; -} - -int gchar_pos(pos_T *pos) - FUNC_ATTR_NONNULL_ARG(1) -{ - // When searching columns is sometimes put at the end of a line. - if (pos->col == MAXCOL) { - return NUL; - } - return utf_ptr2char(ml_get_pos(pos)); -} - -/* - * check_status: called when the status bars for the buffer 'buf' - * need to be updated - */ -void check_status(buf_T *buf) -{ - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf && wp->w_status_height) { - wp->w_redr_status = TRUE; - if (must_redraw < VALID) { - must_redraw = VALID; - } - } - } -} - -/// Ask for a reply from the user, 'y' or 'n' -/// -/// No other characters are accepted, the message is repeated until a valid -/// reply is entered or <C-c> is hit. -/// -/// @param[in] str Prompt: question to ask user. Is always followed by -/// " (y/n)?". -/// @param[in] direct Determines what function to use to get user input. If -/// true then ui_inchar() will be used, otherwise vgetc(). -/// I.e. when direct is true then characters are obtained -/// directly from the user without buffers involved. -/// -/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. -int ask_yesno(const char *const str, const bool direct) -{ - const int save_State = State; - - no_wait_return++; - State = CONFIRM; // Mouse behaves like with :confirm. - setmouse(); // Disable mouse in xterm. - no_mapping++; - - int r = ' '; - while (r != 'y' && r != 'n') { - // Same highlighting as for wait_return. - smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str); - if (direct) { - r = get_keystroke(NULL); - } else { - r = plain_vgetc(); - } - if (r == Ctrl_C || r == ESC) { - r = 'n'; - } - msg_putchar(r); // Show what you typed. - ui_flush(); - } - no_wait_return--; - State = save_State; - setmouse(); - no_mapping--; - - return r; -} - -/* - * Return TRUE if "c" is a mouse key. - */ -int is_mouse_key(int c) -{ - return c == K_LEFTMOUSE - || c == K_LEFTMOUSE_NM - || c == K_LEFTDRAG - || c == K_LEFTRELEASE - || c == K_LEFTRELEASE_NM - || c == K_MOUSEMOVE - || c == K_MIDDLEMOUSE - || c == K_MIDDLEDRAG - || c == K_MIDDLERELEASE - || c == K_RIGHTMOUSE - || c == K_RIGHTDRAG - || c == K_RIGHTRELEASE - || c == K_MOUSEDOWN - || c == K_MOUSEUP - || c == K_MOUSELEFT - || c == K_MOUSERIGHT - || c == K_X1MOUSE - || c == K_X1DRAG - || c == K_X1RELEASE - || c == K_X2MOUSE - || c == K_X2DRAG - || c == K_X2RELEASE; -} - -/* - * Get a key stroke directly from the user. - * Ignores mouse clicks and scrollbar events, except a click for the left - * button (used at the more prompt). - * Doesn't use vgetc(), because it syncs undo and eats mapped characters. - * Disadvantage: typeahead is ignored. - * Translates the interrupt character for unix to ESC. - */ -int get_keystroke(MultiQueue *events) -{ - char_u *buf = NULL; - int buflen = 150; - int maxlen; - int len = 0; - int n; - int save_mapped_ctrl_c = mapped_ctrl_c; - int waited = 0; - - mapped_ctrl_c = 0; // mappings are not used here - for (;; ) { - // flush output before waiting - ui_flush(); - // Leave some room for check_termcode() to insert a key code into (max - // 5 chars plus NUL). And fix_input_buffer() can triple the number of - // bytes. - maxlen = (buflen - 6 - len) / 3; - if (buf == NULL) { - buf = xmalloc((size_t)buflen); - } else if (maxlen < 10) { - // Need some more space. This might happen when receiving a long - // escape sequence. - buflen += 100; - buf = xrealloc(buf, (size_t)buflen); - maxlen = (buflen - 6 - len) / 3; - } - - // First time: blocking wait. Second time: wait up to 100ms for a - // terminal code to complete. - n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events); - if (n > 0) { - // Replace zero and CSI by a special key code. - n = fix_input_buffer(buf + len, n); - len += n; - waited = 0; - } else if (len > 0) { - ++waited; // keep track of the waiting time - } - if (n > 0) { // found a termcode: adjust length - len = n; - } - if (len == 0) { // nothing typed yet - continue; - } - - // Handle modifier and/or special key code. - n = buf[0]; - if (n == K_SPECIAL) { - n = TO_SPECIAL(buf[1], buf[2]); - if (buf[1] == KS_MODIFIER - || n == K_IGNORE - || (is_mouse_key(n) && n != K_LEFTMOUSE)) { - if (buf[1] == KS_MODIFIER) { - mod_mask = buf[2]; - } - len -= 3; - if (len > 0) { - memmove(buf, buf + 3, (size_t)len); - } - continue; - } - break; - } - if (MB_BYTE2LEN(n) > len) { - // more bytes to get. - continue; - } - buf[len >= buflen ? buflen - 1 : len] = NUL; - n = utf_ptr2char(buf); - break; - } - xfree(buf); - - mapped_ctrl_c = save_mapped_ctrl_c; - return n; -} - -/// Get a number from the user. -/// When "mouse_used" is not NULL allow using the mouse. -/// -/// @param colon allow colon to abort -int get_number(int colon, int *mouse_used) -{ - int n = 0; - int c; - int typed = 0; - - if (mouse_used != NULL) { - *mouse_used = FALSE; - } - - // When not printing messages, the user won't know what to type, return a - // zero (as if CR was hit). - if (msg_silent != 0) { - return 0; - } - - no_mapping++; - for (;; ) { - ui_cursor_goto(msg_row, msg_col); - c = safe_vgetc(); - if (ascii_isdigit(c)) { - n = n * 10 + c - '0'; - msg_putchar(c); - ++typed; - } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) { - if (typed > 0) { - msg_puts("\b \b"); - --typed; - } - n /= 10; - } else if (mouse_used != NULL && c == K_LEFTMOUSE) { - *mouse_used = TRUE; - n = mouse_row + 1; - break; - } else if (n == 0 && c == ':' && colon) { - stuffcharReadbuff(':'); - if (!exmode_active) { - cmdline_row = msg_row; - } - skip_redraw = true; // skip redraw once - do_redraw = false; - break; - } else if (c == Ctrl_C || c == ESC || c == 'q') { - n = 0; - break; - } else if (c == CAR || c == NL) { - break; - } - } - no_mapping--; - return n; -} - -/* - * Ask the user to enter a number. - * When "mouse_used" is not NULL allow using the mouse and in that case return - * the line number. - */ -int prompt_for_number(int *mouse_used) -{ - int i; - int save_cmdline_row; - int save_State; - - // When using ":silent" assume that <CR> was entered. - if (mouse_used != NULL) { - msg_puts(_("Type number and <Enter> or click with the mouse " - "(q or empty cancels): ")); - } else { - msg_puts(_("Type number and <Enter> (q or empty cancels): ")); - } - - /* Set the state such that text can be selected/copied/pasted and we still - * get mouse events. */ - save_cmdline_row = cmdline_row; - cmdline_row = 0; - save_State = State; - State = ASKMORE; // prevents a screen update when using a timer - // May show different mouse shape. - setmouse(); - - i = get_number(TRUE, mouse_used); - if (KeyTyped) { - // don't call wait_return() now - if (msg_row > 0) { - cmdline_row = msg_row - 1; - } - need_wait_return = false; - msg_didany = false; - msg_didout = false; - } else { - cmdline_row = save_cmdline_row; - } - State = save_State; - // May need to restore mouse shape. - setmouse(); - - return i; -} - -void msgmore(long n) -{ - long pn; - - if (global_busy // no messages now, wait until global is finished - || !messaging()) { // 'lazyredraw' set, don't do messages now - return; - } - - // We don't want to overwrite another important message, but do overwrite - // a previous "more lines" or "fewer lines" message, so that "5dd" and - // then "put" reports the last action. - if (keep_msg != NULL && !keep_msg_more) { - return; - } - - if (n > 0) { - pn = n; - } else { - pn = -n; - } - - if (pn > p_report) { - if (n > 0) { - vim_snprintf(msg_buf, MSG_BUF_LEN, - NGETTEXT("%ld more line", "%ld more lines", pn), - pn); - } else { - vim_snprintf(msg_buf, MSG_BUF_LEN, - NGETTEXT("%ld line less", "%ld fewer lines", pn), - pn); - } - if (got_int) { - xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN); - } - if (msg(msg_buf)) { - set_keep_msg((char_u *)msg_buf, 0); - keep_msg_more = true; - } - } -} - -/* - * flush map and typeahead buffers and give a warning for an error - */ -void beep_flush(void) -{ - if (emsg_silent == 0) { - flush_buffers(FLUSH_MINIMAL); - vim_beep(BO_ERROR); - } -} - -// Give a warning for an error -// val is one of the BO_ values, e.g., BO_OPER -void vim_beep(unsigned val) -{ - called_vim_beep = true; - - if (emsg_silent == 0) { - if (!((bo_flags & val) || (bo_flags & BO_ALL))) { - static int beeps = 0; - static uint64_t start_time = 0; - - // Only beep up to three times per half a second, - // otherwise a sequence of beeps would freeze Vim. - if (start_time == 0 || os_hrtime() - start_time > 500000000u) { - beeps = 0; - start_time = os_hrtime(); - } - beeps++; - if (beeps <= 3) { - if (p_vb) { - ui_call_visual_bell(); - } else { - ui_call_bell(); - } - } - } - - // When 'debug' contains "beep" produce a message. If we are sourcing - // a script or executing a function give the user a hint where the beep - // comes from. - if (vim_strchr(p_debug, 'e') != NULL) { - msg_source(HL_ATTR(HLF_W)); - msg_attr(_("Beep!"), HL_ATTR(HLF_W)); - } - } -} - -#if defined(EXITFREE) - -void free_users(void) -{ - ga_clear_strings(&ga_users); -} - -#endif - -/* - * Find all user names for user completion. - * Done only once and then cached. - */ -static void init_users(void) -{ - static int lazy_init_done = FALSE; - - if (lazy_init_done) { - return; - } - - lazy_init_done = TRUE; - - os_get_usernames(&ga_users); -} - -/* - * Function given to ExpandGeneric() to obtain an user names. - */ -char_u *get_users(expand_T *xp, int idx) -{ - init_users(); - if (idx < ga_users.ga_len) { - return ((char_u **)ga_users.ga_data)[idx]; - } - return NULL; -} - -/* - * Check whether name matches a user name. Return: - * 0 if name does not match any user name. - * 1 if name partially matches the beginning of a user name. - * 2 is name fully matches a user name. - */ -int match_user(char_u *name) -{ - int n = (int)STRLEN(name); - int result = 0; - - init_users(); - for (int i = 0; i < ga_users.ga_len; i++) { - if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) { - return 2; // full match - } - if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) { - result = 1; // partial match - } - } - return result; -} - -/// Preserve files and exit. -/// @note IObuff must contain a message. -/// @note This may be called from deadly_signal() in a signal handler, avoid -/// unsafe functions, such as allocating memory. -void preserve_exit(void) - FUNC_ATTR_NORETURN -{ - // 'true' when we are sure to exit, e.g., after a deadly signal - static bool really_exiting = false; - - // Prevent repeated calls into this method. - if (really_exiting) { - if (input_global_fd() >= 0) { - // normalize stream (#2598) - stream_set_blocking(input_global_fd(), true); - } - exit(2); - } - - really_exiting = true; - // Ignore SIGHUP while we are already exiting. #9274 - signal_reject_deadly(); - mch_errmsg(IObuff); - mch_errmsg("\n"); - ui_flush(); - - ml_close_notmod(); // close all not-modified buffers - - FOR_ALL_BUFFERS(buf) { - if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) { - mch_errmsg("Vim: preserving files...\r\n"); - ui_flush(); - ml_sync_all(false, false, true); // preserve all swap files - break; - } - } - - ml_close_all(false); // close all memfiles, without deleting - - mch_errmsg("Vim: Finished.\r\n"); - - getout(1); -} - -/* - * Check for CTRL-C pressed, but only once in a while. - * Should be used instead of os_breakcheck() for functions that check for - * each line in the file. Calling os_breakcheck() each time takes too much - * time, because it can be a system call. - */ - -#ifndef BREAKCHECK_SKIP -# define BREAKCHECK_SKIP 1000 -#endif - -static int breakcheck_count = 0; - -void line_breakcheck(void) -{ - if (++breakcheck_count >= BREAKCHECK_SKIP) { - breakcheck_count = 0; - os_breakcheck(); - } -} - -/* - * Like line_breakcheck() but check 10 times less often. - */ -void fast_breakcheck(void) -{ - if (++breakcheck_count >= BREAKCHECK_SKIP * 10) { - breakcheck_count = 0; - os_breakcheck(); - } -} - -// Like line_breakcheck() but check 100 times less often. -void veryfast_breakcheck(void) -{ - if (++breakcheck_count >= BREAKCHECK_SKIP * 100) { - breakcheck_count = 0; - os_breakcheck(); - } -} - -/// os_call_shell() wrapper. Handles 'verbose', :profile, and v:shell_error. -/// Invalidates cached tags. -/// -/// @return shell command exit code -int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) -{ - int retval; - proftime_T wait_time; - - if (p_verbose > 3) { - verbose_enter(); - smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd); - msg_putchar('\n'); - verbose_leave(); - } - - if (do_profiling == PROF_YES) { - prof_child_enter(&wait_time); - } - - if (*p_sh == NUL) { - emsg(_(e_shellempty)); - retval = -1; - } else { - // The external command may update a tags file, clear cached tags. - tag_freematch(); - - retval = os_call_shell(cmd, opts, extra_shell_arg); - } - - set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T)retval); - if (do_profiling == PROF_YES) { - prof_child_exit(&wait_time); - } - - return retval; -} - -/// Get the stdout of an external command. -/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not -/// NULL store the length there. -/// -/// @param cmd command to execute -/// @param infile optional input file name -/// @param flags can be kShellOptSilent or 0 -/// @param ret_len length of the stdout -/// -/// @return an allocated string, or NULL for error. -char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len) -{ - char_u *buffer = NULL; - - if (check_secure()) { - return NULL; - } - - // get a name for the temp file - char_u *tempname = vim_tempname(); - if (tempname == NULL) { - emsg(_(e_notmp)); - return NULL; - } - - // Add the redirection stuff - char_u *command = make_filter_cmd(cmd, infile, tempname); - - /* - * Call the shell to execute the command (errors are ignored). - * Don't check timestamps here. - */ - ++no_check_timestamps; - call_shell(command, kShellOptDoOut | kShellOptExpand | flags, NULL); - --no_check_timestamps; - - xfree(command); - - // read the names from the file into memory - FILE *fd = os_fopen((char *)tempname, READBIN); - - if (fd == NULL) { - semsg(_(e_notopen), tempname); - goto done; - } - - fseek(fd, 0L, SEEK_END); - size_t len = (size_t)ftell(fd); // get size of temp file - fseek(fd, 0L, SEEK_SET); - - buffer = xmalloc(len + 1); - size_t i = fread((char *)buffer, 1, len, fd); - fclose(fd); - os_remove((char *)tempname); - if (i != len) { - semsg(_(e_notread), tempname); - XFREE_CLEAR(buffer); - } else if (ret_len == NULL) { - // Change NUL into SOH, otherwise the string is truncated. - for (i = 0; i < len; ++i) { - if (buffer[i] == NUL) { - buffer[i] = 1; - } - } - - buffer[len] = NUL; // make sure the buffer is terminated - } else { - *ret_len = len; - } - -done: - xfree(tempname); - return buffer; -} - -/* - * Free the list of files returned by expand_wildcards() or other expansion - * functions. - */ -void FreeWild(int count, char_u **files) -{ - if (count <= 0 || files == NULL) { - return; - } - while (count--) { - xfree(files[count]); - } - xfree(files); -} - -/* - * Return TRUE when need to go to Insert mode because of 'insertmode'. - * Don't do this when still processing a command or a mapping. - * Don't do this when inside a ":normal" command. - */ -int goto_im(void) -{ - return p_im && stuff_empty() && typebuf_typed(); -} - -/// Put the timestamp of an undo header in "buf[buflen]" in a nice format. -void add_time(char_u *buf, size_t buflen, time_t tt) -{ - struct tm curtime; - - if (time(NULL) - tt >= 100) { - os_localtime_r(&tt, &curtime); - if (time(NULL) - tt < (60L * 60L * 12L)) { - // within 12 hours - (void)strftime((char *)buf, buflen, "%H:%M:%S", &curtime); - } else { - // longer ago - (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime); - } - } else { - int64_t seconds = time(NULL) - tt; - vim_snprintf((char *)buf, buflen, - NGETTEXT("%" PRId64 " second ago", - "%" PRId64 " seconds ago", (uint32_t)seconds), - seconds); - } -} diff --git a/src/nvim/misc1.h b/src/nvim/misc1.h deleted file mode 100644 index 4ce142c4c5..0000000000 --- a/src/nvim/misc1.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef NVIM_MISC1_H -#define NVIM_MISC1_H - -#include "nvim/os/shell.h" -#include "nvim/vim.h" - -// flags for open_line() -#define OPENLINE_DELSPACES 1 // delete spaces after cursor -#define OPENLINE_DO_COM 2 // format comments -#define OPENLINE_KEEPTRAIL 4 // keep trailing spaces -#define OPENLINE_MARKFIX 8 // fix mark positions -#define OPENLINE_COM_LIST 16 // format comments with list/2nd line indent - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "misc1.h.generated.h" -#endif -#endif // NVIM_MISC1_H diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index f02c000e82..5d007fb173 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -10,7 +10,6 @@ #include "nvim/diff.h" #include "nvim/fold.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/os_unix.h" @@ -31,6 +30,32 @@ static linenr_T orig_topline = 0; static int orig_topfill = 0; +/// Return true if "c" is a mouse key. +bool is_mouse_key(int c) +{ + return c == K_LEFTMOUSE + || c == K_LEFTMOUSE_NM + || c == K_LEFTDRAG + || c == K_LEFTRELEASE + || c == K_LEFTRELEASE_NM + || c == K_MOUSEMOVE + || c == K_MIDDLEMOUSE + || c == K_MIDDLEDRAG + || c == K_MIDDLERELEASE + || c == K_RIGHTMOUSE + || c == K_RIGHTDRAG + || c == K_RIGHTRELEASE + || c == K_MOUSEDOWN + || c == K_MOUSEUP + || c == K_MOUSELEFT + || c == K_MOUSERIGHT + || c == K_X1MOUSE + || c == K_X1DRAG + || c == K_X1RELEASE + || c == K_X2MOUSE + || c == K_X2DRAG + || c == K_X2RELEASE; +} /// Move the cursor to the specified row and column on the screen. /// Change current window if necessary. Returns an integer with the /// CURSOR_MOVED bit set if the cursor has moved or unset otherwise. @@ -245,7 +270,7 @@ retnomove: // Scroll by however many rows outside the window we are. if (row < 0) { count = 0; - for (first = true; curwin->w_topline > 1; ) { + for (first = true; curwin->w_topline > 1;) { if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { count++; } else { @@ -270,7 +295,7 @@ retnomove: row = 0; } else if (row >= curwin->w_height_inner) { count = 0; - for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count; ) { + for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count;) { if (curwin->w_topfill > 0) { ++count; } else { @@ -448,7 +473,7 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp) fp = topframe; *rowp -= firstwin->w_winrow; - for (;; ) { + for (;;) { if (fp->fr_layout == FR_LEAF) { break; } diff --git a/src/nvim/move.c b/src/nvim/move.c index 5114cd6d8a..15ba6645f5 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -23,9 +23,9 @@ #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/fold.h" +#include "nvim/getchar.h" #include "nvim/mbyte.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/plines.h" @@ -382,8 +382,8 @@ static bool check_top_offset(void) topline_back(curwin, &loff); // Stop when included a line above the window. if (loff.lnum < curwin->w_topline - || (loff.lnum == curwin->w_topline && - loff.fill > 0)) { + || (loff.lnum == curwin->w_topline + && loff.fill > 0)) { break; } n += loff.height; @@ -1011,7 +1011,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, col -= wp->w_leftcol; if (col >= 0 && col < wp->w_width) { - coloff = col - scol + (local ? 0 : wp->w_wincol) + 1; + coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_border_adj[3]) + 1; } else { scol = ccol = ecol = 0; // character is left or right of the window @@ -1022,7 +1022,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, } } } - *rowp = (local ? 0 : wp->w_winrow) + row + rowoff; + *rowp = (local ? 0 : wp->w_winrow + wp->w_border_adj[0]) + row + rowoff; *scolp = scol + coloff; *ccolp = ccol + coloff; *ecolp = ecol + coloff; @@ -1661,7 +1661,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) boff.fill = curwin->w_topfill; boff.lnum = curwin->w_topline - 1; int i; - for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; ) { + for (i = 0; i < scrolled && boff.lnum < curwin->w_botline;) { botline_forw(curwin, &boff); i += boff.height; ++line_count; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index a1a1f0f8c0..299651ee97 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -24,7 +24,6 @@ #include "nvim/map.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/os/input.h" diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index f805858904..32014fcf2b 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -92,15 +92,15 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) break; } #define STR_CASE(type, attr, obj, dest, conv) \ -case type: { \ - dest = conv(((String) { \ + case type: { \ + dest = conv(((String) { \ .size = obj->via.attr.size, \ .data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \ ? xmemdupz("", 0) \ : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), \ })); \ - break; \ -} + break; \ + } STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ) STR_CASE(MSGPACK_OBJECT_BIN, bin, cur.mobj, *cur.aobj, STRING_OBJ) case MSGPACK_OBJECT_ARRAY: { @@ -143,10 +143,10 @@ case type: { \ const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key; switch (key->type) { #define ID(x) x - STR_CASE(MSGPACK_OBJECT_STR, str, key, - cur.aobj->data.dictionary.items[idx].key, ID) - STR_CASE(MSGPACK_OBJECT_BIN, bin, key, - cur.aobj->data.dictionary.items[idx].key, ID) + STR_CASE(MSGPACK_OBJECT_STR, str, key, + cur.aobj->data.dictionary.items[idx].key, ID) + STR_CASE(MSGPACK_OBJECT_BIN, bin, key, + cur.aobj->data.dictionary.items[idx].key, ID) #undef ID case MSGPACK_OBJECT_NIL: case MSGPACK_OBJECT_BOOLEAN: diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 1effeefd32..3246596f16 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -31,8 +31,8 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" -#include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/log.h" #include "nvim/main.h" @@ -40,7 +40,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -84,12 +83,6 @@ typedef struct normal_state { pos_T old_pos; } NormalState; -/* - * The Visual area is remembered for reselection. - */ -static int resel_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V -static linenr_T resel_VIsual_line_count; // number of lines -static colnr_T resel_VIsual_vcol; // nr of cols or end col static int VIsual_mode_orig = NUL; // saved Visual mode @@ -236,7 +229,7 @@ static const struct nv_cmd { { 'N', nv_next, 0, SEARCH_REV }, { 'O', nv_open, 0, 0 }, { 'P', nv_put, 0, 0 }, - { 'Q', nv_exmode, NV_NCW, 0 }, + { 'Q', nv_regreplay, 0, 0 }, { 'R', nv_Replace, 0, false }, { 'S', nv_subst, NV_KEEPREG, 0 }, { 'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD }, @@ -487,6 +480,7 @@ static void normal_prepare(NormalState *s) if (finish_op != c) { ui_cursor_shape(); // may show different cursor shape } + trigger_modechanged(); // When not finishing an operator and no register name typed, reset the count. if (!finish_op && !s->oa.regname) { @@ -570,8 +564,8 @@ static bool normal_need_additional_char(NormalState *s) // // TODO(tarruda): Visual state needs to be refactored into a // separate state that "inherits" from normal state. - || ((cmdchar == 'a' || cmdchar == 'i') && - (pending_op || VIsual_active))); + || ((cmdchar == 'a' || cmdchar == 'i') + && (pending_op || VIsual_active))); } static bool normal_need_redraw_mode_message(NormalState *s) @@ -928,6 +922,7 @@ normal_end: // Reset finish_op, in case it was set s->c = finish_op; finish_op = false; + trigger_modechanged(); // Redraw the cursor with another shape, if we were in Operator-pending // mode or did a replace command. if (s->c || s->ca.cmdchar == 'r') { @@ -965,6 +960,7 @@ normal_end: && s->oa.regname == 0) { if (restart_VIsual_select == 1) { VIsual_select = true; + trigger_modechanged(); showmode(); restart_VIsual_select = 0; } @@ -1452,758 +1448,6 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount) *set_prevcount = false; // only set v:prevcount once } -// Handle an operator after Visual mode or when the movement is finished. -// "gui_yank" is true when yanking text for the clipboard. -void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) -{ - oparg_T *oap = cap->oap; - pos_T old_cursor; - bool empty_region_error; - int restart_edit_save; - int lbr_saved = curwin->w_p_lbr; - - - // The visual area is remembered for redo - static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V - static linenr_T redo_VIsual_line_count; // number of lines - static colnr_T redo_VIsual_vcol; // number of cols or end column - static long redo_VIsual_count; // count for Visual operator - static int redo_VIsual_arg; // extra argument - bool include_line_break = false; - - old_cursor = curwin->w_cursor; - - /* - * If an operation is pending, handle it... - */ - if ((finish_op - || VIsual_active) - && oap->op_type != OP_NOP) { - // Yank can be redone when 'y' is in 'cpoptions', but not when yanking - // for the clipboard. - const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; - - // Avoid a problem with unwanted linebreaks in block mode - if (curwin->w_p_lbr) { - curwin->w_valid &= ~VALID_VIRTCOL; - } - curwin->w_p_lbr = false; - oap->is_VIsual = VIsual_active; - if (oap->motion_force == 'V') { - oap->motion_type = kMTLineWise; - } else if (oap->motion_force == 'v') { - // If the motion was linewise, "inclusive" will not have been set. - // Use "exclusive" to be consistent. Makes "dvj" work nice. - if (oap->motion_type == kMTLineWise) { - oap->inclusive = false; - } else if (oap->motion_type == kMTCharWise) { - // If the motion already was charwise, toggle "inclusive" - oap->inclusive = !oap->inclusive; - } - oap->motion_type = kMTCharWise; - } else if (oap->motion_force == Ctrl_V) { - // Change line- or charwise motion into Visual block mode. - if (!VIsual_active) { - VIsual_active = true; - VIsual = oap->start; - } - VIsual_mode = Ctrl_V; - VIsual_select = false; - VIsual_reselect = false; - } - - // Only redo yank when 'y' flag is in 'cpoptions'. - // Never redo "zf" (define fold). - if ((redo_yank || oap->op_type != OP_YANK) - && ((!VIsual_active || oap->motion_force) - // Also redo Operator-pending Visual mode mappings. - || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) - && oap->op_type != OP_COLON)) - && cap->cmdchar != 'D' - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search - /* - * If 'cpoptions' does not contain 'r', insert the search - * pattern to really repeat the same command. - */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) { - AppendToRedobuffLit(cap->searchbuf, -1); - } - AppendToRedobuff(NL_STR); - } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { - // do_cmdline() has stored the first typed line in - // "repeat_cmdline". When several lines are typed repeating - // won't be possible. - if (repeat_cmdline == NULL) { - ResetRedobuff(); - } else { - AppendToRedobuffLit(repeat_cmdline, -1); - AppendToRedobuff(NL_STR); - XFREE_CLEAR(repeat_cmdline); - } - } - } - - if (redo_VIsual_busy) { - /* Redo of an operation on a Visual area. Use the same size from - * redo_VIsual_line_count and redo_VIsual_vcol. */ - oap->start = curwin->w_cursor; - curwin->w_cursor.lnum += redo_VIsual_line_count - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } - VIsual_mode = redo_VIsual_mode; - if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { - if (VIsual_mode == 'v') { - if (redo_VIsual_line_count <= 1) { - validate_virtcol(); - curwin->w_curswant = - curwin->w_virtcol + redo_VIsual_vcol - 1; - } else { - curwin->w_curswant = redo_VIsual_vcol; - } - } else { - curwin->w_curswant = MAXCOL; - } - coladvance(curwin->w_curswant); - } - cap->count0 = redo_VIsual_count; - cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); - } else if (VIsual_active) { - if (!gui_yank) { - // Save the current VIsual area for '< and '> marks, and "gv" - curbuf->b_visual.vi_start = VIsual; - curbuf->b_visual.vi_end = curwin->w_cursor; - curbuf->b_visual.vi_mode = VIsual_mode; - if (VIsual_mode_orig != NUL) { - curbuf->b_visual.vi_mode = VIsual_mode_orig; - VIsual_mode_orig = NUL; - } - curbuf->b_visual.vi_curswant = curwin->w_curswant; - curbuf->b_visual_mode_eval = VIsual_mode; - } - - // In Select mode, a linewise selection is operated upon like a - // charwise selection. - // Special case: gH<Del> deletes the last line. - if (VIsual_select && VIsual_mode == 'V' - && cap->oap->op_type != OP_DELETE) { - if (lt(VIsual, curwin->w_cursor)) { - VIsual.col = 0; - curwin->w_cursor.col = - (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); - } else { - curwin->w_cursor.col = 0; - VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); - } - VIsual_mode = 'v'; - } - /* If 'selection' is "exclusive", backup one character for - * charwise selections. */ - else if (VIsual_mode == 'v') { - include_line_break = - unadjust_for_sel(); - } - - oap->start = VIsual; - if (VIsual_mode == 'V') { - oap->start.col = 0; - oap->start.coladd = 0; - } - } - - /* - * Set oap->start to the first position of the operated text, oap->end - * to the end of the operated text. w_cursor is equal to oap->start. - */ - if (lt(oap->start, curwin->w_cursor)) { - // Include folded lines completely. - if (!VIsual_active) { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { - oap->start.col = 0; - } - if ((curwin->w_cursor.col > 0 - || oap->inclusive - || oap->motion_type == kMTLineWise) - && hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum)) { - curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr()); - } - } - oap->end = curwin->w_cursor; - curwin->w_cursor = oap->start; - - /* w_virtcol may have been updated; if the cursor goes back to its - * previous position w_virtcol becomes invalid and isn't updated - * automatically. */ - curwin->w_valid &= ~VALID_VIRTCOL; - } else { - // Include folded lines completely. - if (!VIsual_active && oap->motion_type == kMTLineWise) { - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, - NULL)) { - curwin->w_cursor.col = 0; - } - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { - oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); - } - } - oap->end = oap->start; - oap->start = curwin->w_cursor; - } - - // Just in case lines were deleted that make the position invalid. - check_pos(curwin->w_buffer, &oap->end); - oap->line_count = oap->end.lnum - oap->start.lnum + 1; - - // Set "virtual_op" before resetting VIsual_active. - virtual_op = virtual_active(); - - if (VIsual_active || redo_VIsual_busy) { - get_op_vcol(oap, redo_VIsual_vcol, true); - - if (!redo_VIsual_busy && !gui_yank) { - /* - * Prepare to reselect and redo Visual: this is based on the - * size of the Visual text - */ - resel_VIsual_mode = VIsual_mode; - if (curwin->w_curswant == MAXCOL) { - resel_VIsual_vcol = MAXCOL; - } else { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->end), - NULL, NULL, &oap->end_vcol); - } - if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->start), - &oap->start_vcol, NULL, NULL); - } - resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; - } else { - resel_VIsual_vcol = oap->end_vcol; - } - } - resel_VIsual_line_count = oap->line_count; - } - - // can't redo yank (unless 'y' is in 'cpoptions') and ":" - if ((redo_yank || oap->op_type != OP_YANK) - && oap->op_type != OP_COLON - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC - && oap->motion_force == NUL) { - /* Prepare for redoing. Only use the nchar field for "r", - * otherwise it might be the second char of the operator. */ - if (cap->cmdchar == 'g' && (cap->nchar == 'n' - || cap->nchar == 'N')) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { - int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; - - // reverse what nv_replace() did - if (nchar == REPLACE_CR_NCHAR) { - nchar = CAR; - } else if (nchar == REPLACE_NL_NCHAR) { - nchar = NL; - } - prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), - get_extra_op_char(oap->op_type), nchar); - } - if (!redo_VIsual_busy) { - redo_VIsual_mode = resel_VIsual_mode; - redo_VIsual_vcol = resel_VIsual_vcol; - redo_VIsual_line_count = resel_VIsual_line_count; - redo_VIsual_count = cap->count0; - redo_VIsual_arg = cap->arg; - } - } - - // oap->inclusive defaults to true. - // If oap->end is on a NUL (empty line) oap->inclusive becomes - // false. This makes "d}P" and "v}dP" work the same. - if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { - oap->inclusive = true; - } - if (VIsual_mode == 'V') { - oap->motion_type = kMTLineWise; - } else if (VIsual_mode == 'v') { - oap->motion_type = kMTCharWise; - if (*ml_get_pos(&(oap->end)) == NUL - && (include_line_break || !virtual_op)) { - oap->inclusive = false; - // Try to include the newline, unless it's an operator - // that works on lines only. - if (*p_sel != 'o' - && !op_on_lines(oap->op_type) - && oap->end.lnum < curbuf->b_ml.ml_line_count) { - oap->end.lnum++; - oap->end.col = 0; - oap->end.coladd = 0; - oap->line_count++; - } - } - } - - redo_VIsual_busy = false; - - /* - * Switch Visual off now, so screen updating does - * not show inverted text when the screen is redrawn. - * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is - * no screen redraw, so it is done here to remove the inverted - * part. - */ - if (!gui_yank) { - VIsual_active = false; - setmouse(); - mouse_dragging = 0; - may_clear_cmdline(); - if ((oap->op_type == OP_YANK - || oap->op_type == OP_COLON - || oap->op_type == OP_FUNCTION - || oap->op_type == OP_FILTER) - && oap->motion_force == NUL) { - // Make sure redrawing is correct. - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - } - } - - // Include the trailing byte of a multi-byte char. - if (oap->inclusive) { - const int l = utfc_ptr2len(ml_get_pos(&oap->end)); - if (l > 1) { - oap->end.col += l - 1; - } - } - curwin->w_set_curswant = true; - - /* - * oap->empty is set when start and end are the same. The inclusive - * flag affects this too, unless yanking and the end is on a NUL. - */ - oap->empty = (oap->motion_type != kMTLineWise - && (!oap->inclusive - || (oap->op_type == OP_YANK - && gchar_pos(&oap->end) == NUL)) - && equalpos(oap->start, oap->end) - && !(virtual_op && oap->start.coladd != oap->end.coladd) - ); - /* - * For delete, change and yank, it's an error to operate on an - * empty region, when 'E' included in 'cpoptions' (Vi compatible). - */ - empty_region_error = (oap->empty - && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); - - /* Force a redraw when operating on an empty Visual region, when - * 'modifiable is off or creating a fold. */ - if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) - || oap->op_type == OP_FOLD - )) { - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - - /* - * If the end of an operator is in column one while oap->motion_type - * is kMTCharWise and oap->inclusive is false, we put op_end after the last - * character in the previous line. If op_start is on or before the - * first non-blank in the line, the operator becomes linewise - * (strange, but that's the way vi does it). - */ - if (oap->motion_type == kMTCharWise - && oap->inclusive == false - && !(cap->retval & CA_NO_ADJ_OP_END) - && oap->end.col == 0 - && (!oap->is_VIsual || *p_sel == 'o') - && oap->line_count > 1) { - oap->end_adjusted = true; // remember that we did this - oap->line_count--; - oap->end.lnum--; - if (inindent(0)) { - oap->motion_type = kMTLineWise; - } else { - oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) { - --oap->end.col; - oap->inclusive = true; - } - } - } else { - oap->end_adjusted = false; - } - - switch (oap->op_type) { - case OP_LSHIFT: - case OP_RSHIFT: - op_shift(oap, true, - oap->is_VIsual ? (int)cap->count1 : - 1); - auto_format(false, true); - break; - - case OP_JOIN_NS: - case OP_JOIN: - if (oap->line_count < 2) { - oap->line_count = 2; - } - if (curwin->w_cursor.lnum + oap->line_count - 1 > - curbuf->b_ml.ml_line_count) { - beep_flush(); - } else { - do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, - true, true, true); - auto_format(false, true); - } - break; - - case OP_DELETE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - (void)op_delete(oap); - // save cursor line for undo if it wasn't saved yet - if (oap->motion_type == kMTLineWise - && has_format_option(FO_AUTO) - && u_save_cursor() == OK) { - auto_format(false, true); - } - } - break; - - case OP_YANK: - if (empty_region_error) { - if (!gui_yank) { - vim_beep(BO_OPER); - CancelRedo(); - } - } else { - curwin->w_p_lbr = lbr_saved; - oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank, false); - } - check_cursor_col(); - break; - - case OP_CHANGE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once and not when typed and - * 'insertmode' isn't set. */ - if (p_im || !KeyTyped) { - restart_edit_save = restart_edit; - } else { - restart_edit_save = 0; - } - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - // Reset finish_op now, don't want it set inside edit(). - finish_op = false; - if (op_change(oap)) { // will call edit() - cap->retval |= CA_COMMAND_BUSY; - } - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } - } - break; - - case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { - AppendToRedobuff("!\r"); // Use any last used !cmd. - } else { - bangredo = true; // do_bang() will put cmd in redo buffer. - } - FALLTHROUGH; - - case OP_INDENT: - case OP_COLON: - - /* - * If 'equalprg' is empty, do the indenting internally. - */ - if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { - if (curbuf->b_p_lisp) { - op_reindent(oap, get_lisp_indent); - break; - } - op_reindent(oap, - *curbuf->b_p_inde != NUL ? get_expr_indent : - get_c_indent); - break; - } - - op_colon(oap); - break; - - case OP_TILDE: - case OP_UPPER: - case OP_LOWER: - case OP_ROT13: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - op_tilde(oap); - } - check_cursor_col(); - break; - - case OP_FORMAT: - if (*curbuf->b_p_fex != NUL) { - op_formatexpr(oap); // use expression - } else { - if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { - op_colon(oap); // use external command - } else { - op_format(oap, false); // use internal function - } - } - break; - - case OP_FORMAT2: - op_format(oap, true); // use internal function - break; - - case OP_FUNCTION: - // Restore linebreak, so that when the user edits it looks as - // before. - curwin->w_p_lbr = lbr_saved; - op_function(oap); // call 'operatorfunc' - break; - - case OP_INSERT: - case OP_APPEND: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once. */ - restart_edit_save = restart_edit; - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_insert(oap, cap->count1); - - // Reset linebreak, so that formatting works correctly. - curwin->w_p_lbr = false; - - /* TODO: when inserting in several lines, should format all - * the lines. */ - auto_format(false, true); - - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } else { - cap->retval |= CA_COMMAND_BUSY; - } - } - break; - - case OP_REPLACE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_replace(oap, cap->nchar); - } - break; - - case OP_FOLD: - VIsual_reselect = false; // don't reselect now - foldCreate(curwin, oap->start, oap->end); - break; - - case OP_FOLDOPEN: - case OP_FOLDOPENREC: - case OP_FOLDCLOSE: - case OP_FOLDCLOSEREC: - VIsual_reselect = false; // don't reselect now - opFoldRange(oap->start, oap->end, - oap->op_type == OP_FOLDOPEN - || oap->op_type == OP_FOLDOPENREC, - oap->op_type == OP_FOLDOPENREC - || oap->op_type == OP_FOLDCLOSEREC, - oap->is_VIsual); - break; - - case OP_FOLDDEL: - case OP_FOLDDELREC: - VIsual_reselect = false; // don't reselect now - deleteFold(curwin, oap->start.lnum, oap->end.lnum, - oap->op_type == OP_FOLDDELREC, oap->is_VIsual); - break; - - case OP_NR_ADD: - case OP_NR_SUB: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - VIsual_active = true; - curwin->w_p_lbr = lbr_saved; - op_addsub(oap, cap->count1, redo_VIsual_arg); - VIsual_active = false; - } - check_cursor_col(); - break; - default: - clearopbeep(oap); - } - virtual_op = kNone; - if (!gui_yank) { - /* - * if 'sol' not set, go back to old column for some commands - */ - if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted - && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT - || oap->op_type == OP_DELETE)) { - curwin->w_p_lbr = false; - coladvance(curwin->w_curswant = old_col); - } - } else { - curwin->w_cursor = old_cursor; - } - clearop(oap); - motion_force = NUL; - } - curwin->w_p_lbr = lbr_saved; -} - -/* - * Handle indent and format operators and visual mode ":". - */ -static void op_colon(oparg_T *oap) -{ - stuffcharReadbuff(':'); - if (oap->is_VIsual) { - stuffReadbuff("'<,'>"); - } else { - // Make the range look nice, so it can be repeated. - if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else { - stuffnumReadbuff((long)oap->start.lnum); - } - if (oap->end.lnum != oap->start.lnum) { - stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { - stuffcharReadbuff('$'); - } else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff(".+"); - stuffnumReadbuff(oap->line_count - 1); - } else { - stuffnumReadbuff((long)oap->end.lnum); - } - } - } - if (oap->op_type != OP_COLON) { - stuffReadbuff("!"); - } - if (oap->op_type == OP_INDENT) { - stuffReadbuff((const char *)get_equalprg()); - stuffReadbuff("\n"); - } else if (oap->op_type == OP_FORMAT) { - if (*curbuf->b_p_fp != NUL) { - stuffReadbuff((const char *)curbuf->b_p_fp); - } else if (*p_fp != NUL) { - stuffReadbuff((const char *)p_fp); - } else { - stuffReadbuff("fmt"); - } - stuffReadbuff("\n']"); - } - - /* - * do_cmdline() does the rest - */ -} - -/* - * Handle the "g@" operator: call 'operatorfunc'. - */ -static void op_function(const oparg_T *oap) - FUNC_ATTR_NONNULL_ALL -{ - const TriState save_virtual_op = virtual_op; - const bool save_finish_op = finish_op; - - if (*p_opfunc == NUL) { - emsg(_("E774: 'operatorfunc' is empty")); - } else { - // Set '[ and '] marks to text to be operated on. - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - if (oap->motion_type != kMTLineWise && !oap->inclusive) { - // Exclude the end position. - decl(&curbuf->b_op_end); - } - - typval_T argv[2]; - argv[0].v_type = VAR_STRING; - argv[1].v_type = VAR_UNKNOWN; - argv[0].vval.v_string = - (char_u *)(((const char *const[]) { - [kMTBlockWise] = "block", - [kMTLineWise] = "line", - [kMTCharWise] = "char", - })[oap->motion_type]); - - // Reset virtual_op so that 'virtualedit' can be changed in the - // function. - virtual_op = kNone; - - // Reset finish_op so that mode() returns the right value. - finish_op = false; - - (void)call_func_retnr(p_opfunc, 1, argv); - - virtual_op = save_virtual_op; - finish_op = save_finish_op; - } -} - // Move the current tab to tab in same column as mouse or to end of the // tabline if there is no tab there. static void move_tab_to_mouse(void) @@ -2283,7 +1527,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) save_cursor = curwin->w_cursor; - for (;; ) { + for (;;) { which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag); if (is_drag) { /* If the next character is the same mouse event then use that @@ -3066,6 +2310,7 @@ void end_visual_mode(void) may_clear_cmdline(); adjust_cursor_eol(); + trigger_modechanged(); } /* @@ -3092,6 +2337,14 @@ void reset_VIsual(void) } } +void restore_visual_mode(void) +{ + if (VIsual_mode_orig != NUL) { + curbuf->b_visual.vi_mode = VIsual_mode_orig; + VIsual_mode_orig = NUL; + } +} + // Check for a balloon-eval special item to include when searching for an // identifier. When "dir" is BACKWARD "ptr[-1]" must be valid! // Returns true if the character at "*ptr" should be included. @@ -3270,7 +2523,7 @@ static void prep_redo_cmd(cmdarg_T *cap) * Prepare for redo of any command. * Note that only the last argument can be a multi-byte char. */ -static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) +void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) { ResetRedobuff(); if (regname != 0) { // yank from specified buffer @@ -3327,7 +2580,7 @@ static bool checkclearopq(oparg_T *oap) return true; } -static void clearop(oparg_T *oap) +void clearop(oparg_T *oap) { oap->op_type = OP_NOP; oap->regname = 0; @@ -3336,7 +2589,7 @@ static void clearop(oparg_T *oap) motion_force = NUL; } -static void clearopbeep(oparg_T *oap) +void clearopbeep(oparg_T *oap) { clearop(oap); beep_flush(); @@ -3366,7 +2619,7 @@ static void unshift_special(cmdarg_T *cap) /// If the mode is currently displayed clear the command line or update the /// command displayed. -static void may_clear_cmdline(void) +void may_clear_cmdline(void) { if (mode_displayed) { // unshow visual mode later @@ -3928,7 +3181,7 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_ // Search forward for the identifier, ignore comment lines. clearpos(&found_pos); - for (;; ) { + for (;;) { t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD, pat, 1L, searchflags, RE_LAST, NULL); if (curwin->w_cursor.lnum >= old_pos.lnum) { @@ -4307,7 +3560,7 @@ static void nv_zet(cmdarg_T *cap) return; } n = nchar - '0'; - for (;; ) { + for (;;) { no_mapping++; nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); @@ -4775,15 +4028,18 @@ dozet: /* * "Q" command. */ -static void nv_exmode(cmdarg_T *cap) +static void nv_regreplay(cmdarg_T *cap) { - /* - * Ignore 'Q' in Visual mode, just give a beep. - */ - if (VIsual_active) { - vim_beep(BO_EX); - } else if (!checkclearop(cap->oap)) { - do_exmode(); + if (checkclearop(cap->oap)) { + return; + } + + while (cap->count1-- && !got_int) { + if (do_execreg(reg_recorded, false, false, false) == false) { + clearopbeep(cap->oap); + break; + } + line_breakcheck(); } } @@ -4851,6 +4107,7 @@ static void nv_ctrlg(cmdarg_T *cap) { if (VIsual_active) { // toggle Selection/Visual mode VIsual_select = !VIsual_select; + trigger_modechanged(); showmode(); } else if (!checkclearop(cap->oap)) { // print full name if count given or :cd used @@ -4894,6 +4151,7 @@ static void nv_ctrlo(cmdarg_T *cap) { if (VIsual_active && VIsual_select) { VIsual_select = false; + trigger_modechanged(); showmode(); restart_VIsual_select = 2; // restart Select mode later } else { @@ -5341,8 +4599,8 @@ static void nv_right(cmdarg_T *cap) for (n = cap->count1; n > 0; --n) { if ((!PAST_LINE && oneright() == false) - || (PAST_LINE && - *get_cursor_pos_ptr() == NUL)) { + || (PAST_LINE + && *get_cursor_pos_ptr() == NUL)) { // <Space> wraps to next line if 'whichwrap' has 's'. // 'l' wraps to next line if 'whichwrap' has 'l'. // CURS_RIGHT wraps to next line if 'whichwrap' has '>'. @@ -5838,7 +5096,7 @@ static void nv_brackets(cmdarg_T *cap) pos = NULL; } while (n > 0) { - for (;; ) { + for (;;) { if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) { // if not found anything, that's an error if (pos == NULL) { @@ -6680,6 +5938,7 @@ static void nv_visual(cmdarg_T *cap) // or char/line mode VIsual_mode = cap->cmdchar; showmode(); + trigger_modechanged(); } redraw_curbuf_later(INVERTED); // update the inversion } else { // start Visual mode @@ -6793,6 +6052,7 @@ static void n_start_visual_mode(int c) foldAdjustVisual(); + trigger_modechanged(); setmouse(); // Check for redraw after changing the state. conceal_check_cursor_line(); @@ -7688,8 +6948,8 @@ static void adjust_cursor(oparg_T *oap) // - 'virtualedit' is not "all" and not "onemore". if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL && (!VIsual_active || *p_sel == 'o') - && !virtual_active() && - (ve_flags & VE_ONEMORE) == 0) { + && !virtual_active() + && (ve_flags & VE_ONEMORE) == 0) { curwin->w_cursor.col--; // prevent cursor from moving on the trail byte mb_adjust_cursor(); @@ -7730,7 +6990,7 @@ static void adjust_for_sel(cmdarg_T *cap) * Should check VIsual_mode before calling this. * Returns true when backed up to the previous line. */ -static bool unadjust_for_sel(void) +bool unadjust_for_sel(void) { pos_T *pp; @@ -8320,71 +7580,6 @@ static void nv_open(cmdarg_T *cap) } } -/// Calculate start/end virtual columns for operating in block mode. -/// -/// @param initial when true: adjust position for 'selectmode' -static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) -{ - colnr_T start; - colnr_T end; - - if (VIsual_mode != Ctrl_V - || (!initial && oap->end.col < curwin->w_width_inner)) { - return; - } - - oap->motion_type = kMTBlockWise; - - // prevent from moving onto a trail byte - mark_mb_adjustpos(curwin->w_buffer, &oap->end); - - getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); - if (!redo_VIsual_busy) { - getvvcol(curwin, &(oap->end), &start, NULL, &end); - - if (start < oap->start_vcol) { - oap->start_vcol = start; - } - if (end > oap->end_vcol) { - if (initial && *p_sel == 'e' - && start >= 1 - && start - 1 >= oap->end_vcol) { - oap->end_vcol = start - 1; - } else { - oap->end_vcol = end; - } - } - } - - // if '$' was used, get oap->end_vcol from longest line - if (curwin->w_curswant == MAXCOL) { - curwin->w_cursor.col = MAXCOL; - oap->end_vcol = 0; - for (curwin->w_cursor.lnum = oap->start.lnum; - curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { - getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); - if (end > oap->end_vcol) { - oap->end_vcol = end; - } - } - } else if (redo_VIsual_busy) { - oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; - } - - // Correct oap->end.col and oap->start.col to be the - // upper-left and lower-right corner of the block area. - // - // (Actually, this does convert column positions into character - // positions) - curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); - oap->end = curwin->w_cursor; - - curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); - oap->start = curwin->w_cursor; -} - // Handle an arbitrary event in normal mode static void nv_event(cmdarg_T *cap) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 7d7db2a8a6..c6f9c5f04f 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -27,7 +27,9 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" +#include "nvim/indent_c.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/macros.h" @@ -36,7 +38,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -378,7 +380,7 @@ static void shift_block(oparg_T *oap, int amount) bd.startspaces = 0; } } - for (; ascii_iswhite(*bd.textstart); ) { + for (; ascii_iswhite(*bd.textstart);) { // TODO: is passing bd.textstart for start of the line OK? incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, (bd.start_vcol)); total += incr; @@ -910,13 +912,14 @@ int do_record(int c) showmode(); regname = c; retval = OK; - } - } else { // stop recording - /* - * Get the recorded key hits. K_SPECIAL and CSI will be escaped, this - * needs to be removed again to put it in a register. exec_reg then - * adds the escaping back later. - */ + apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf); + } + } else { // stop recording + // Get the recorded key hits. K_SPECIAL and CSI will be escaped, this + // needs to be removed again to put it in a register. exec_reg then + // adds the escaping back later. + apply_autocmds(EVENT_RECORDINGLEAVE, NULL, NULL, false, curbuf); + reg_recorded = reg_recording; reg_recording = 0; if (ui_has(kUIMessages)) { showmode(); @@ -930,10 +933,8 @@ int do_record(int c) // Remove escaping for CSI and K_SPECIAL in multi-byte chars. vim_unescape_csi(p); - /* - * We don't want to change the default register here, so save and - * restore the current register name. - */ + // We don't want to change the default register here, so save and + // restore the current register name. old_y_previous = y_previous; retval = stuff_yank(regname, p); @@ -1768,7 +1769,7 @@ static void replace_character(int c) /* * Replace a whole area with one character. */ -int op_replace(oparg_T *oap, int c) +static int op_replace(oparg_T *oap, int c) { int n, numc; int num_chars; @@ -2037,7 +2038,7 @@ void op_tilde(oparg_T *oap) did_change = swapchars(oap->op_type, &pos, oap->end.col - pos.col + 1); } else { - for (;; ) { + for (;;) { did_change |= swapchars(oap->op_type, &pos, pos.lnum == oap->end.lnum ? oap->end.col + 1 : (int)STRLEN(ml_get_pos(&pos))); @@ -2162,7 +2163,8 @@ void op_insert(oparg_T *oap, long count1) { long ins_len, pre_textlen = 0; char_u *firstline, *ins_text; - colnr_T ind_pre = 0; + colnr_T ind_pre_col = 0, ind_post_col; + int ind_pre_vcol = 0, ind_post_vcol = 0; struct block_def bd; int i; pos_T t1; @@ -2196,7 +2198,8 @@ void op_insert(oparg_T *oap, long count1) // Get the info about the block before entering the text block_prep(oap, &bd, oap->start.lnum, true); // Get indent information - ind_pre = (colnr_T)getwhitecols_curline(); + ind_pre_col = (colnr_T)getwhitecols_curline(); + ind_pre_vcol = get_indent(); firstline = ml_get(oap->start.lnum) + bd.textcol; if (oap->op_type == OP_APPEND) { @@ -2261,10 +2264,11 @@ void op_insert(oparg_T *oap, long count1) // if indent kicked in, the firstline might have changed // but only do that, if the indent actually increased - const colnr_T ind_post = (colnr_T)getwhitecols_curline(); - if (curbuf->b_op_start.col > ind_pre && ind_post > ind_pre) { - bd.textcol += ind_post - ind_pre; - bd.start_vcol += ind_post - ind_pre; + ind_post_col = (colnr_T)getwhitecols_curline(); + if (curbuf->b_op_start.col > ind_pre_col && ind_post_col > ind_pre_col) { + bd.textcol += ind_post_col - ind_pre_col; + ind_post_vcol = get_indent(); + bd.start_vcol += ind_post_vcol - ind_pre_vcol; did_indent = true; } @@ -2297,12 +2301,26 @@ void op_insert(oparg_T *oap, long count1) } } - /* - * Spaces and tabs in the indent may have changed to other spaces and - * tabs. Get the starting column again and correct the length. - * Don't do this when "$" used, end-of-line will have changed. - */ + // Spaces and tabs in the indent may have changed to other spaces and + // tabs. Get the starting column again and correct the length. + // Don't do this when "$" used, end-of-line will have changed. + // + // if indent was added and the inserted text was after the indent, + // correct the selection for the new indent. + if (did_indent && bd.textcol - ind_post_col > 0) { + oap->start.col += ind_post_col - ind_pre_col; + oap->start_vcol += ind_post_vcol - ind_pre_vcol; + oap->end.col += ind_post_col - ind_pre_col; + oap->end_vcol += ind_post_vcol - ind_pre_vcol; + } block_prep(oap, &bd2, oap->start.lnum, true); + if (did_indent && bd.textcol - ind_post_col > 0) { + // undo for where "oap" is used below + oap->start.col -= ind_post_col - ind_pre_col; + oap->start_vcol -= ind_post_vcol - ind_pre_vcol; + oap->end.col -= ind_post_col - ind_pre_col; + oap->end_vcol -= ind_post_vcol - ind_pre_vcol; + } if (!bd.is_MAX || bd2.textlen < bd.textlen) { if (oap->op_type == OP_APPEND) { pre_textlen += bd2.textlen - bd.textlen; @@ -2792,8 +2810,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) recursive = true; + save_v_event_T save_v_event; // Set the v:event dictionary with information about the yank. - dict_T *dict = get_vim_var_dict(VV_EVENT); + dict_T *dict = get_v_event(&save_v_event); // The yanked text contents. list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); @@ -2830,7 +2849,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } @@ -2987,7 +3006,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* For the = register we need to split the string at NL * characters. * Loop twice: count the number of lines and save them. */ - for (;; ) { + for (;;) { y_size = 0; ptr = insert_string; while (ptr != NULL) { @@ -3204,7 +3223,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // get the old line and advance to the position to insert at oldp = get_cursor_line_ptr(); oldlen = STRLEN(oldp); - for (ptr = oldp; vcol < col && *ptr; ) { + for (ptr = oldp; vcol < col && *ptr;) { // Count a tab for what it's worth (if list mode not on) incr = lbr_chartabsize_adv(oldp, &ptr, vcol); vcol += incr; @@ -3340,6 +3359,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (y_type == kMTCharWise && y_size == 1) { linenr_T end_lnum = 0; // init for gcc linenr_T start_lnum = lnum; + int first_byte_off = 0; if (VIsual_active) { end_lnum = curbuf->b_visual.vi_end.lnum; @@ -3386,6 +3406,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } STRMOVE(ptr, oldp + col); ml_replace(lnum, newp, false); + + // compute the byte offset for the last character + first_byte_off = utf_head_off(newp, ptr - 1); + // Place cursor on last putted char. if (lnum == curwin->w_cursor.lnum) { // make sure curwin->w_virtcol is updated @@ -3405,10 +3429,15 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) lnum--; } + // put '] at the first byte of the last character curbuf->b_op_end = curwin->w_cursor; + curbuf->b_op_end.col -= first_byte_off; + // For "CTRL-O p" in Insert mode, put cursor after last char if (totlen && (restart_edit != 0 || (flags & PUT_CURSEND))) { curwin->w_cursor.col++; + } else { + curwin->w_cursor.col -= first_byte_off; } } else { // Insert at least one line. When y_type is kMTCharWise, break the first @@ -3520,12 +3549,13 @@ error: curbuf->b_op_start.lnum, nr_lines, true); } - // put '] mark at last inserted character + // Put the '] mark on the first byte of the last inserted character. + // Correct the length for change in indent. curbuf->b_op_end.lnum = lnum; - // correct length for change in indent col = (colnr_T)STRLEN(y_array[y_size - 1]) - lendiff; if (col > 1) { - curbuf->b_op_end.col = col - 1; + curbuf->b_op_end.col = col - 1 - utf_head_off(y_array[y_size - 1], + y_array[y_size - 1] + col - 1); } else { curbuf->b_op_end.col = 0; } @@ -3936,8 +3966,8 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions || (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100)) && (!has_format_option(FO_MBYTE_JOIN2) || (utf_ptr2char(curr) < 0x100 && !utf_eat_space(endcurr1)) - || (endcurr1 < 0x100 && - !utf_eat_space(utf_ptr2char(curr))))) { + || (endcurr1 < 0x100 + && !utf_eat_space(utf_ptr2char(curr))))) { // don't add a space if the line is ending in a space if (endcurr1 == ' ') { endcurr1 = endcurr2; @@ -4148,7 +4178,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in /// Implementation of the format operator 'gq'. /// /// @param keep_cursor keep cursor on same text char -void op_format(oparg_T *oap, int keep_cursor) +static void op_format(oparg_T *oap, int keep_cursor) { long old_line_count = curbuf->b_ml.ml_line_count; @@ -4216,7 +4246,7 @@ void op_format(oparg_T *oap, int keep_cursor) /* * Implementation of the format operator 'gq' for when using 'formatexpr'. */ -void op_formatexpr(oparg_T *oap) +static void op_formatexpr(oparg_T *oap) { if (oap->is_VIsual) { // When there is no change: need to remove the Visual selection @@ -5631,7 +5661,7 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *c varnumber_T chars = 0; int is_word = 0; - for (i = 0; i < limit && line[i] != NUL; ) { + for (i = 0; i < limit && line[i] != NUL;) { if (is_word) { if (ascii_isspace(line[i])) { words++; @@ -5907,6 +5937,791 @@ void cursor_pos_info(dict_T *dict) } } +// Handle indent and format operators and visual mode ":". +static void op_colon(oparg_T *oap) +{ + stuffcharReadbuff(':'); + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else { + stuffnumReadbuff((long)oap->start.lnum); + } + if (oap->end.lnum != oap->start.lnum) { + stuffcharReadbuff(','); + if (oap->end.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { + stuffcharReadbuff('$'); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); + stuffnumReadbuff(oap->line_count - 1); + } else { + stuffnumReadbuff((long)oap->end.lnum); + } + } + } + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } + if (oap->op_type == OP_INDENT) { + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); + } else if (oap->op_type == OP_FORMAT) { + if (*curbuf->b_p_fp != NUL) { + stuffReadbuff((const char *)curbuf->b_p_fp); + } else if (*p_fp != NUL) { + stuffReadbuff((const char *)p_fp); + } else { + stuffReadbuff("fmt"); + } + stuffReadbuff("\n']"); + } + + // do_cmdline() does the rest +} + +// Handle the "g@" operator: call 'operatorfunc'. +static void op_function(const oparg_T *oap) + FUNC_ATTR_NONNULL_ALL +{ + const TriState save_virtual_op = virtual_op; + const bool save_finish_op = finish_op; + + if (*p_opfunc == NUL) { + emsg(_("E774: 'operatorfunc' is empty")); + } else { + // Set '[ and '] marks to text to be operated on. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + if (oap->motion_type != kMTLineWise && !oap->inclusive) { + // Exclude the end position. + decl(&curbuf->b_op_end); + } + + typval_T argv[2]; + argv[0].v_type = VAR_STRING; + argv[1].v_type = VAR_UNKNOWN; + argv[0].vval.v_string = + (char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]); + + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. + virtual_op = kNone; + + // Reset finish_op so that mode() returns the right value. + finish_op = false; + + (void)call_func_retnr(p_opfunc, 1, argv); + + virtual_op = save_virtual_op; + finish_op = save_finish_op; + } +} + +/// Calculate start/end virtual columns for operating in block mode. +/// +/// @param initial when true: adjust position for 'selectmode' +static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) +{ + colnr_T start; + colnr_T end; + + if (VIsual_mode != Ctrl_V + || (!initial && oap->end.col < curwin->w_width_inner)) { + return; + } + + oap->motion_type = kMTBlockWise; + + // prevent from moving onto a trail byte + mark_mb_adjustpos(curwin->w_buffer, &oap->end); + + getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); + if (!redo_VIsual_busy) { + getvvcol(curwin, &(oap->end), &start, NULL, &end); + + if (start < oap->start_vcol) { + oap->start_vcol = start; + } + if (end > oap->end_vcol) { + if (initial && *p_sel == 'e' + && start >= 1 + && start - 1 >= oap->end_vcol) { + oap->end_vcol = start - 1; + } else { + oap->end_vcol = end; + } + } + } + + // if '$' was used, get oap->end_vcol from longest line + if (curwin->w_curswant == MAXCOL) { + curwin->w_cursor.col = MAXCOL; + oap->end_vcol = 0; + for (curwin->w_cursor.lnum = oap->start.lnum; + curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); + if (end > oap->end_vcol) { + oap->end_vcol = end; + } + } + } else if (redo_VIsual_busy) { + oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; + } + + // Correct oap->end.col and oap->start.col to be the + // upper-left and lower-right corner of the block area. + // + // (Actually, this does convert column positions into character + // positions) + curwin->w_cursor.lnum = oap->end.lnum; + coladvance(oap->end_vcol); + oap->end = curwin->w_cursor; + + curwin->w_cursor = oap->start; + coladvance(oap->start_vcol); + oap->start = curwin->w_cursor; +} + +// Handle an operator after Visual mode or when the movement is finished. +// "gui_yank" is true when yanking text for the clipboard. +void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) +{ + oparg_T *oap = cap->oap; + pos_T old_cursor; + bool empty_region_error; + int restart_edit_save; + int lbr_saved = curwin->w_p_lbr; + + + // The visual area is remembered for redo + static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V + static linenr_T redo_VIsual_line_count; // number of lines + static colnr_T redo_VIsual_vcol; // number of cols or end column + static long redo_VIsual_count; // count for Visual operator + static int redo_VIsual_arg; // extra argument + bool include_line_break = false; + + old_cursor = curwin->w_cursor; + + // If an operation is pending, handle it... + if ((finish_op + || VIsual_active) + && oap->op_type != OP_NOP) { + // Yank can be redone when 'y' is in 'cpoptions', but not when yanking + // for the clipboard. + const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; + + // Avoid a problem with unwanted linebreaks in block mode + if (curwin->w_p_lbr) { + curwin->w_valid &= ~VALID_VIRTCOL; + } + curwin->w_p_lbr = false; + oap->is_VIsual = VIsual_active; + if (oap->motion_force == 'V') { + oap->motion_type = kMTLineWise; + } else if (oap->motion_force == 'v') { + // If the motion was linewise, "inclusive" will not have been set. + // Use "exclusive" to be consistent. Makes "dvj" work nice. + if (oap->motion_type == kMTLineWise) { + oap->inclusive = false; + } else if (oap->motion_type == kMTCharWise) { + // If the motion already was charwise, toggle "inclusive" + oap->inclusive = !oap->inclusive; + } + oap->motion_type = kMTCharWise; + } else if (oap->motion_force == Ctrl_V) { + // Change line- or charwise motion into Visual block mode. + if (!VIsual_active) { + VIsual_active = true; + VIsual = oap->start; + } + VIsual_mode = Ctrl_V; + VIsual_select = false; + VIsual_reselect = false; + } + + // Only redo yank when 'y' flag is in 'cpoptions'. + // Never redo "zf" (define fold). + if ((redo_yank || oap->op_type != OP_YANK) + && ((!VIsual_active || oap->motion_force) + // Also redo Operator-pending Visual mode mappings. + || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) + && oap->op_type != OP_COLON)) + && cap->cmdchar != 'D' + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search + // If 'cpoptions' does not contain 'r', insert the search + // pattern to really repeat the same command. + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { + AppendToRedobuffLit(cap->searchbuf, -1); + } + AppendToRedobuff(NL_STR); + } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { + // do_cmdline() has stored the first typed line in + // "repeat_cmdline". When several lines are typed repeating + // won't be possible. + if (repeat_cmdline == NULL) { + ResetRedobuff(); + } else { + AppendToRedobuffLit(repeat_cmdline, -1); + AppendToRedobuff(NL_STR); + XFREE_CLEAR(repeat_cmdline); + } + } + } + + if (redo_VIsual_busy) { + // Redo of an operation on a Visual area. Use the same size from + // redo_VIsual_line_count and redo_VIsual_vcol. + oap->start = curwin->w_cursor; + curwin->w_cursor.lnum += redo_VIsual_line_count - 1; + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } + VIsual_mode = redo_VIsual_mode; + if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { + if (VIsual_mode == 'v') { + if (redo_VIsual_line_count <= 1) { + validate_virtcol(); + curwin->w_curswant = + curwin->w_virtcol + redo_VIsual_vcol - 1; + } else { + curwin->w_curswant = redo_VIsual_vcol; + } + } else { + curwin->w_curswant = MAXCOL; + } + coladvance(curwin->w_curswant); + } + cap->count0 = redo_VIsual_count; + cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); + } else if (VIsual_active) { + if (!gui_yank) { + // Save the current VIsual area for '< and '> marks, and "gv" + curbuf->b_visual.vi_start = VIsual; + curbuf->b_visual.vi_end = curwin->w_cursor; + curbuf->b_visual.vi_mode = VIsual_mode; + restore_visual_mode(); + curbuf->b_visual.vi_curswant = curwin->w_curswant; + curbuf->b_visual_mode_eval = VIsual_mode; + } + + // In Select mode, a linewise selection is operated upon like a + // charwise selection. + // Special case: gH<Del> deletes the last line. + if (VIsual_select && VIsual_mode == 'V' + && cap->oap->op_type != OP_DELETE) { + if (lt(VIsual, curwin->w_cursor)) { + VIsual.col = 0; + curwin->w_cursor.col = + (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); + } else { + curwin->w_cursor.col = 0; + VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); + } + VIsual_mode = 'v'; + } else if (VIsual_mode == 'v') { + // If 'selection' is "exclusive", backup one character for + // charwise selections. + include_line_break = + unadjust_for_sel(); + } + + oap->start = VIsual; + if (VIsual_mode == 'V') { + oap->start.col = 0; + oap->start.coladd = 0; + } + } + + // Set oap->start to the first position of the operated text, oap->end + // to the end of the operated text. w_cursor is equal to oap->start. + if (lt(oap->start, curwin->w_cursor)) { + // Include folded lines completely. + if (!VIsual_active) { + if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { + oap->start.col = 0; + } + if ((curwin->w_cursor.col > 0 + || oap->inclusive + || oap->motion_type == kMTLineWise) + && hasFolding(curwin->w_cursor.lnum, NULL, + &curwin->w_cursor.lnum)) { + curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } + } + oap->end = curwin->w_cursor; + curwin->w_cursor = oap->start; + + // w_virtcol may have been updated; if the cursor goes back to its + // previous position w_virtcol becomes invalid and isn't updated + // automatically. + curwin->w_valid &= ~VALID_VIRTCOL; + } else { + // Include folded lines completely. + if (!VIsual_active && oap->motion_type == kMTLineWise) { + if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + NULL)) { + curwin->w_cursor.col = 0; + } + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { + oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } + } + oap->end = oap->start; + oap->start = curwin->w_cursor; + } + + // Just in case lines were deleted that make the position invalid. + check_pos(curwin->w_buffer, &oap->end); + oap->line_count = oap->end.lnum - oap->start.lnum + 1; + + // Set "virtual_op" before resetting VIsual_active. + virtual_op = virtual_active(); + + if (VIsual_active || redo_VIsual_busy) { + get_op_vcol(oap, redo_VIsual_vcol, true); + + if (!redo_VIsual_busy && !gui_yank) { + // Prepare to reselect and redo Visual: this is based on the + // size of the Visual text + resel_VIsual_mode = VIsual_mode; + if (curwin->w_curswant == MAXCOL) { + resel_VIsual_vcol = MAXCOL; + } else { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->end), + NULL, NULL, &oap->end_vcol); + } + if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->start), + &oap->start_vcol, NULL, NULL); + } + resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; + } else { + resel_VIsual_vcol = oap->end_vcol; + } + } + resel_VIsual_line_count = oap->line_count; + } + + // can't redo yank (unless 'y' is in 'cpoptions') and ":" + if ((redo_yank || oap->op_type != OP_YANK) + && oap->op_type != OP_COLON + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC + && oap->motion_force == NUL) { + // Prepare for redoing. Only use the nchar field for "r", + // otherwise it might be the second char of the operator. + if (cap->cmdchar == 'g' && (cap->nchar == 'n' + || cap->nchar == 'N')) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { + int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; + + // reverse what nv_replace() did + if (nchar == REPLACE_CR_NCHAR) { + nchar = CAR; + } else if (nchar == REPLACE_NL_NCHAR) { + nchar = NL; + } + prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), + get_extra_op_char(oap->op_type), nchar); + } + if (!redo_VIsual_busy) { + redo_VIsual_mode = resel_VIsual_mode; + redo_VIsual_vcol = resel_VIsual_vcol; + redo_VIsual_line_count = resel_VIsual_line_count; + redo_VIsual_count = cap->count0; + redo_VIsual_arg = cap->arg; + } + } + + // oap->inclusive defaults to true. + // If oap->end is on a NUL (empty line) oap->inclusive becomes + // false. This makes "d}P" and "v}dP" work the same. + if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { + oap->inclusive = true; + } + if (VIsual_mode == 'V') { + oap->motion_type = kMTLineWise; + } else if (VIsual_mode == 'v') { + oap->motion_type = kMTCharWise; + if (*ml_get_pos(&(oap->end)) == NUL + && (include_line_break || !virtual_op)) { + oap->inclusive = false; + // Try to include the newline, unless it's an operator + // that works on lines only. + if (*p_sel != 'o' + && !op_on_lines(oap->op_type) + && oap->end.lnum < curbuf->b_ml.ml_line_count) { + oap->end.lnum++; + oap->end.col = 0; + oap->end.coladd = 0; + oap->line_count++; + } + } + } + + redo_VIsual_busy = false; + + // Switch Visual off now, so screen updating does + // not show inverted text when the screen is redrawn. + // With OP_YANK and sometimes with OP_COLON and OP_FILTER there is + // no screen redraw, so it is done here to remove the inverted + // part. + if (!gui_yank) { + VIsual_active = false; + setmouse(); + mouse_dragging = 0; + may_clear_cmdline(); + if ((oap->op_type == OP_YANK + || oap->op_type == OP_COLON + || oap->op_type == OP_FUNCTION + || oap->op_type == OP_FILTER) + && oap->motion_force == NUL) { + // Make sure redrawing is correct. + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + } + } + + // Include the trailing byte of a multi-byte char. + if (oap->inclusive) { + const int l = utfc_ptr2len(ml_get_pos(&oap->end)); + if (l > 1) { + oap->end.col += l - 1; + } + } + curwin->w_set_curswant = true; + + // oap->empty is set when start and end are the same. The inclusive + // flag affects this too, unless yanking and the end is on a NUL. + oap->empty = (oap->motion_type != kMTLineWise + && (!oap->inclusive + || (oap->op_type == OP_YANK + && gchar_pos(&oap->end) == NUL)) + && equalpos(oap->start, oap->end) + && !(virtual_op && oap->start.coladd != oap->end.coladd)); + // For delete, change and yank, it's an error to operate on an + // empty region, when 'E' included in 'cpoptions' (Vi compatible). + empty_region_error = (oap->empty + && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); + + // Force a redraw when operating on an empty Visual region, when + // 'modifiable is off or creating a fold. + if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) + || oap->op_type == OP_FOLD)) { + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + + // If the end of an operator is in column one while oap->motion_type + // is kMTCharWise and oap->inclusive is false, we put op_end after the last + // character in the previous line. If op_start is on or before the + // first non-blank in the line, the operator becomes linewise + // (strange, but that's the way vi does it). + if (oap->motion_type == kMTCharWise + && oap->inclusive == false + && !(cap->retval & CA_NO_ADJ_OP_END) + && oap->end.col == 0 + && (!oap->is_VIsual || *p_sel == 'o') + && oap->line_count > 1) { + oap->end_adjusted = true; // remember that we did this + oap->line_count--; + oap->end.lnum--; + if (inindent(0)) { + oap->motion_type = kMTLineWise; + } else { + oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col) { + oap->end.col--; + oap->inclusive = true; + } + } + } else { + oap->end_adjusted = false; + } + + switch (oap->op_type) { + case OP_LSHIFT: + case OP_RSHIFT: + op_shift(oap, true, + oap->is_VIsual ? (int)cap->count1 : + 1); + auto_format(false, true); + break; + + case OP_JOIN_NS: + case OP_JOIN: + if (oap->line_count < 2) { + oap->line_count = 2; + } + if (curwin->w_cursor.lnum + oap->line_count - 1 > + curbuf->b_ml.ml_line_count) { + beep_flush(); + } else { + do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, + true, true, true); + auto_format(false, true); + } + break; + + case OP_DELETE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + (void)op_delete(oap); + // save cursor line for undo if it wasn't saved yet + if (oap->motion_type == kMTLineWise + && has_format_option(FO_AUTO) + && u_save_cursor() == OK) { + auto_format(false, true); + } + } + break; + + case OP_YANK: + if (empty_region_error) { + if (!gui_yank) { + vim_beep(BO_OPER); + CancelRedo(); + } + } else { + curwin->w_p_lbr = lbr_saved; + oap->excl_tr_ws = cap->cmdchar == 'z'; + (void)op_yank(oap, !gui_yank, false); + } + check_cursor_col(); + break; + + case OP_CHANGE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once and not when typed and + // 'insertmode' isn't set. + if (p_im || !KeyTyped) { + restart_edit_save = restart_edit; + } else { + restart_edit_save = 0; + } + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + // Reset finish_op now, don't want it set inside edit(). + finish_op = false; + if (op_change(oap)) { // will call edit() + cap->retval |= CA_COMMAND_BUSY; + } + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } + } + break; + + case OP_FILTER: + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } + FALLTHROUGH; + + case OP_INDENT: + case OP_COLON: + + // If 'equalprg' is empty, do the indenting internally. + if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { + if (curbuf->b_p_lisp) { + op_reindent(oap, get_lisp_indent); + break; + } + op_reindent(oap, + *curbuf->b_p_inde != NUL ? get_expr_indent : + get_c_indent); + break; + } + + op_colon(oap); + break; + + case OP_TILDE: + case OP_UPPER: + case OP_LOWER: + case OP_ROT13: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + op_tilde(oap); + } + check_cursor_col(); + break; + + case OP_FORMAT: + if (*curbuf->b_p_fex != NUL) { + op_formatexpr(oap); // use expression + } else { + if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { + op_colon(oap); // use external command + } else { + op_format(oap, false); // use internal function + } + } + break; + + case OP_FORMAT2: + op_format(oap, true); // use internal function + break; + + case OP_FUNCTION: + // Restore linebreak, so that when the user edits it looks as + // before. + curwin->w_p_lbr = lbr_saved; + op_function(oap); // call 'operatorfunc' + break; + + case OP_INSERT: + case OP_APPEND: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once. + restart_edit_save = restart_edit; + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_insert(oap, cap->count1); + + // Reset linebreak, so that formatting works correctly. + curwin->w_p_lbr = false; + + // TODO(brammool): when inserting in several lines, should format all + // the lines. + auto_format(false, true); + + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } else { + cap->retval |= CA_COMMAND_BUSY; + } + } + break; + + case OP_REPLACE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_replace(oap, cap->nchar); + } + break; + + case OP_FOLD: + VIsual_reselect = false; // don't reselect now + foldCreate(curwin, oap->start, oap->end); + break; + + case OP_FOLDOPEN: + case OP_FOLDOPENREC: + case OP_FOLDCLOSE: + case OP_FOLDCLOSEREC: + VIsual_reselect = false; // don't reselect now + opFoldRange(oap->start, oap->end, + oap->op_type == OP_FOLDOPEN + || oap->op_type == OP_FOLDOPENREC, + oap->op_type == OP_FOLDOPENREC + || oap->op_type == OP_FOLDCLOSEREC, + oap->is_VIsual); + break; + + case OP_FOLDDEL: + case OP_FOLDDELREC: + VIsual_reselect = false; // don't reselect now + deleteFold(curwin, oap->start.lnum, oap->end.lnum, + oap->op_type == OP_FOLDDELREC, oap->is_VIsual); + break; + + case OP_NR_ADD: + case OP_NR_SUB: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + VIsual_active = true; + curwin->w_p_lbr = lbr_saved; + op_addsub(oap, cap->count1, redo_VIsual_arg); + VIsual_active = false; + } + check_cursor_col(); + break; + default: + clearopbeep(oap); + } + virtual_op = kNone; + if (!gui_yank) { + // if 'sol' not set, go back to old column for some commands + if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted + && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT + || oap->op_type == OP_DELETE)) { + curwin->w_p_lbr = false; + coladvance(curwin->w_curswant = old_col); + } + } else { + curwin->w_cursor = old_cursor; + } + clearop(oap); + motion_force = NUL; + } + curwin->w_p_lbr = lbr_saved; +} + /// Check if the default register (used in an unnamed paste) should be a /// clipboard register. This happens when `clipboard=unnamed[plus]` is set /// and a provider is available. diff --git a/src/nvim/option.c b/src/nvim/option.c index b7df856949..2ceb1bd992 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -55,7 +55,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -262,6 +261,7 @@ typedef struct vimoption { #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ "i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr," \ + "G:CursorLineSign,O:CursorLineFold" \ "r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \ "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \ "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \ @@ -1233,7 +1233,7 @@ int do_set(char_u *arg, int opt_flags) } errmsg = set_bool_option(opt_idx, varp, (int)value, - opt_flags); + opt_flags); } else { // Numeric or string. if (vim_strchr((const char_u *)"=:&<", nextchar) == NULL || prefix != 1) { @@ -1301,7 +1301,11 @@ int do_set(char_u *arg, int opt_flags) char_u *oldval = NULL; // previous value if *varp char_u *newval; char_u *origval = NULL; + char_u *origval_l = NULL; + char_u *origval_g = NULL; char *saved_origval = NULL; + char *saved_origval_l = NULL; + char *saved_origval_g = NULL; char *saved_newval = NULL; unsigned newlen; int comma; @@ -1319,10 +1323,21 @@ int do_set(char_u *arg, int opt_flags) // new value is valid. oldval = *(char_u **)varp; + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + + // A global-local string option might have an empty + // option as value to indicate that the global + // value should be used. + if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) { + origval_l = origval_g; + } + } + // When setting the local value of a global // option, the old value may be the global value. - if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags - & OPT_LOCAL)) { + if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) { origval = *(char_u **)get_varp(&options[opt_idx]); } else { origval = oldval; @@ -1388,6 +1403,12 @@ int do_set(char_u *arg, int opt_flags) if (origval == oldval) { origval = *(char_u **)varp; } + if (origval_l == oldval) { + origval_l = *(char_u **)varp; + } + if (origval_g == oldval) { + origval_g = *(char_u **)varp; + } oldval = *(char_u **)varp; } /* @@ -1596,6 +1617,8 @@ int do_set(char_u *arg, int opt_flags) // origval may be freed by // did_set_string_option(), make a copy. saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0; + saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : 0; + saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : 0; // newval (and varp) may become invalid if the // buffer is closed by autocommands. @@ -1630,8 +1653,8 @@ int do_set(char_u *arg, int opt_flags) if (errmsg == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_origval, - saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_origval, saved_origval_l, + saved_origval_g, saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -1639,6 +1662,8 @@ int do_set(char_u *arg, int opt_flags) } } xfree(saved_origval); + xfree(saved_origval_l); + xfree(saved_origval_g); xfree(saved_newval); // If error detected, print the error message. @@ -2233,9 +2258,19 @@ static char *set_string_option(const int opt_idx, const char *const value, const ? OPT_GLOBAL : OPT_LOCAL) : opt_flags)); char *const oldval = *varp; + char *oldval_l = NULL; + char *oldval_g = NULL; + + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + oldval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + oldval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + *varp = s; char *const saved_oldval = xstrdup(oldval); + char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup((char *)oldval_l) : 0; + char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup((char *)oldval_g) : 0; char *const saved_newval = xstrdup(s); int value_checked = false; @@ -2249,7 +2284,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const // call autocommand after handling side effects if (r == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g, + saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -2257,6 +2293,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const } } xfree(saved_oldval); + xfree(saved_oldval_l); + xfree(saved_oldval_g); xfree(saved_newval); return r; @@ -2315,8 +2353,8 @@ static bool valid_spellfile(const char_u *val) /// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL /// @param value_checked value was checked to be safe, no need to set P_INSECURE static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_alloced, - char_u *oldval, char *errbuf, size_t errbuflen, - int opt_flags, int *value_checked) + char_u *oldval, char *errbuf, size_t errbuflen, int opt_flags, + int *value_checked) { char *errmsg = NULL; char_u *s, *p; @@ -2419,7 +2457,7 @@ static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_al } } else if (varp == &p_hl) { // 'highlight' - if (strcmp((char *)(*varp), HIGHLIGHT_INIT) != 0) { + if (STRCMP(*varp, HIGHLIGHT_INIT) != 0) { errmsg = e_unsupportedoption; } } else if (varp == &p_jop) { // 'jumpoptions' @@ -2625,7 +2663,7 @@ ambw_end: } } } else if (gvarp == &p_com) { // 'comments' - for (s = *varp; *s; ) { + for (s = *varp; *s;) { while (*s && *s != ':') { if (vim_strchr((char_u *)COM_ALL, *s) == NULL && !ascii_isdigit(*s) && *s != '-') { @@ -2650,24 +2688,38 @@ ambw_end: } s = skip_to_option_part(s); } - } else if (varp == &p_lcs) { // 'listchars' + } else if (varp == &p_lcs) { // global 'listchars' errmsg = set_chars_option(curwin, varp, false); - if (!errmsg) { + if (errmsg == NULL) { + // The current window is set to use the global 'listchars' value. + // So clear the window-local value. + if (!(opt_flags & OPT_GLOBAL)) { + clear_string_option(&curwin->w_p_lcs); + } FOR_ALL_TAB_WINDOWS(tp, wp) { - set_chars_option(wp, &wp->w_p_lcs, true); + // If no error was returned above, we don't expect an error + // here, so ignore the return value. + (void)set_chars_option(wp, &wp->w_p_lcs, true); } + redraw_all_later(NOT_VALID); } - redraw_all_later(NOT_VALID); } else if (varp == &curwin->w_p_lcs) { // local 'listchars' errmsg = set_chars_option(curwin, varp, true); - } else if (varp == &p_fcs) { // 'fillchars' + } else if (varp == &p_fcs) { // global 'fillchars' errmsg = set_chars_option(curwin, varp, false); - if (!errmsg) { + if (errmsg == NULL) { + // The current window is set to use the global 'fillchars' value. + // So clear the window-local value. + if (!(opt_flags & OPT_GLOBAL)) { + clear_string_option(&curwin->w_p_fcs); + } FOR_ALL_TAB_WINDOWS(tp, wp) { - set_chars_option(wp, &wp->w_p_fcs, true); + // If no error was returned above, we don't expect an error + // here, so ignore the return value. + (void)set_chars_option(wp, &wp->w_p_fcs, true); } + redraw_all_later(NOT_VALID); } - redraw_all_later(NOT_VALID); } else if (varp == &curwin->w_p_fcs) { // local 'fillchars' errmsg = set_chars_option(curwin, varp, true); } else if (varp == &p_cedit) { // 'cedit' @@ -2690,7 +2742,7 @@ ambw_end: // there would be a disconnect between the check for P_ALLOCED at the start // of the function and the set of P_ALLOCED at the end of the function. free_oldval = (options[opt_idx].flags & P_ALLOCED); - for (s = p_shada; *s; ) { + for (s = p_shada; *s;) { // Check it's a valid character if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) { errmsg = illegal_char(errbuf, errbuflen, *s); @@ -2735,7 +2787,7 @@ ambw_end: errmsg = N_("E528: Must specify a ' value"); } } else if (gvarp == &p_sbr) { // 'showbreak' - for (s = *varp; *s; ) { + for (s = *varp; *s;) { if (ptr2cells(s) != 1) { errmsg = N_("E595: 'showbreak' contains unprintable or wide character"); } @@ -2879,7 +2931,7 @@ ambw_end: } } else if (gvarp == &p_cpt) { // check if it is a valid value for 'complete' -- Acevedo - for (s = *varp; *s; ) { + for (s = *varp; *s;) { while (*s == ',' || *s == ' ') { s++; } @@ -3355,7 +3407,7 @@ char *check_colorcolumn(win_T *wp) return NULL; // buffer was closed } - for (s = wp->w_p_cc; *s != NUL && count < 255; ) { + for (s = wp->w_p_cc; *s != NUL && count < 255;) { if (*s == '-' || *s == '+') { // -N and +N: add to 'textwidth' col = (*s == '-') ? -1 : 1; @@ -3422,6 +3474,37 @@ void check_blending(win_T *wp) wp->w_p_winbl > 0 || (wp->w_floating && wp->w_float_config.shadow); } +/// Calls mb_cptr2char_adv(p) and returns the character. +/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used. +/// Returns 0 for invalid hex or invalid UTF-8 byte. +static int get_encoded_char_adv(char_u **p) +{ + char_u *s = *p; + + if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) { + int64_t num = 0; + int bytes; + int n; + for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) { + *p += 2; + n = hexhex2nr(*p); + if (n < 0) { + return 0; + } + num = num * 256 + n; + } + *p += 2; + return (int)num; + } + + // TODO(bfredl): use schar_T representation and utfc_ptr2len + int clen = utf_ptr2len(s); + int c = mb_cptr2char_adv((const char_u **)p); + if (clen == 1 && c > 127) { // Invalid UTF-8 byte + return 0; + } + return c; +} /// Handle setting 'listchars' or 'fillchars'. /// Assume monocell characters @@ -3526,26 +3609,21 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) && p[len + 1] != NUL) { c2 = c3 = 0; s = p + len + 1; - - // TODO(bfredl): use schar_T representation and utfc_ptr2len - int c1len = utf_ptr2len(s); - c1 = mb_cptr2char_adv((const char_u **)&s); - if (utf_char2cells(c1) > 1 || (c1len == 1 && c1 > 127)) { + c1 = get_encoded_char_adv(&s); + if (c1 == 0 || utf_char2cells(c1) > 1) { return e_invarg; } if (tab[i].cp == &wp->w_p_lcs_chars.tab2) { if (*s == NUL) { return e_invarg; } - int c2len = utf_ptr2len(s); - c2 = mb_cptr2char_adv((const char_u **)&s); - if (utf_char2cells(c2) > 1 || (c2len == 1 && c2 > 127)) { + c2 = get_encoded_char_adv(&s); + if (c2 == 0 || utf_char2cells(c2) > 1) { return e_invarg; } if (!(*s == ',' || *s == NUL)) { - int c3len = utf_ptr2len(s); - c3 = mb_cptr2char_adv((const char_u **)&s); - if (utf_char2cells(c3) > 1 || (c3len == 1 && c3 > 127)) { + c3 = get_encoded_char_adv(&s); + if (c3 == 0 || utf_char2cells(c3) > 1) { return e_invarg; } } @@ -3578,9 +3656,8 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) last_multispace = p; multispace_len = 0; while (*s != NUL && *s != ',') { - int c1len = utf_ptr2len(s); - c1 = mb_cptr2char_adv((const char_u **)&s); - if (utf_char2cells(c1) > 1 || (c1len == 1 && c1 > 127)) { + c1 = get_encoded_char_adv(&s); + if (c1 == 0 || utf_char2cells(c1) > 1) { return e_invarg; } multispace_len++; @@ -3593,7 +3670,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) } else { int multispace_pos = 0; while (*s != NUL && *s != ',') { - c1 = mb_cptr2char_adv((const char_u **)&s); + c1 = get_encoded_char_adv(&s); if (p == last_multispace) { wp->w_p_lcs_chars.multispace[multispace_pos++] = c1; } @@ -3812,6 +3889,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va const int opt_flags) { int old_value = *(int *)varp; + int old_global_value = 0; // Disallow changing some options from secure mode if ((secure || sandbox != 0) @@ -3819,6 +3897,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va return (char *)e_secure; } + // Save the global value before changing anything. This is needed as for + // a global-only option setting the "local value" in fact sets the global + // value (since there is only one value). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + *(int *)varp = value; // set the new value // Remember where the option was set. set_option_sctx_idx(opt_idx, opt_flags, current_sctx); @@ -4095,20 +4180,35 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va // Don't do this while starting up or recursively. if (!starting && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[2]; + char buf_old_global[2]; char buf_new[2]; char buf_type[7]; - vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", - old_value ? true: false); - vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", - value ? true: false); + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true : false); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%d", old_global_value ? true : false); + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true : false); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4142,7 +4242,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, { char *errmsg = NULL; long old_value = *(long *)varp; - long old_Rows = Rows; // remember old Rows + long old_global_value = 0; // only used when setting a local and global option + long old_Rows = Rows; // remember old Rows long *pp = (long *)varp; // Disallow changing some options from secure mode. @@ -4151,6 +4252,13 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return e_secure; } + // Save the global value before changing anything. This is needed as for + // a global-only option setting the "local value" infact sets the global + // value (since there is only one value). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + // Many number options assume their value is in the signed int range. if (value < INT_MIN || value > INT_MAX) { return e_invarg; @@ -4495,19 +4603,36 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // Don't do this while starting up, failure or recursively. if (!starting && errmsg == NULL && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[NUMBUFLEN]; + char buf_old_global[NUMBUFLEN]; char buf_new[NUMBUFLEN]; char buf_type[7]; vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%ld", old_global_value); vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4526,7 +4651,15 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return (char *)errmsg; } -static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *newval) +/// Trigger the OptionSet autocommand. +/// "opt_idx" is the index of the option being set. +/// "opt_flags" can be OPT_LOCAL etc. +/// "oldval" the old value +/// "oldval_l" the old local value (only non-NULL if global and local value are set) +/// "oldval_g" the old global value (only non-NULL if global and local value are set) +/// "newval" the new value +static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l, + char *oldval_g, char *newval) { // Don't do this recursively. if (oldval != NULL @@ -4539,8 +4672,24 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, set_vim_var_string(VV_OPTION_OLD, oldval, -1); set_vim_var_string(VV_OPTION_NEW, newval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } } @@ -4732,7 +4881,8 @@ static int findoption(const char *const arg) /// @param stringval NULL when only checking existence /// /// @returns: -/// Number or Toggle option: 1, *numval gets value. +/// Toggle option: 2, *numval gets value. +/// Number option: 1, *numval gets value. /// String option: 0, *stringval gets allocated string. /// Hidden Number or Toggle option: -1. /// hidden String option: -2. @@ -4765,16 +4915,18 @@ int get_option_value(const char *name, long *numval, char_u **stringval, int opt } if (options[opt_idx].flags & P_NUM) { *numval = *(long *)varp; + return 1; + } + + // Special case: 'modified' is b_changed, but we also want to consider + // it set when 'ff' or 'fenc' changed. + if ((int *)varp == &curbuf->b_changed) { + *numval = curbufIsChanged(); } else { - // Special case: 'modified' is b_changed, but we also want to consider - // it set when 'ff' or 'fenc' changed. - if ((int *)varp == &curbuf->b_changed) { - *numval = curbufIsChanged(); - } else { - *numval = (long)*(int *)varp; // NOLINT(whitespace/cast) - } + *numval = (long)*(int *)varp; // NOLINT(whitespace/cast) } - return 1; + + return 2; } // Returns the option attributes and its value. Unlike the above function it @@ -4870,7 +5022,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o // only getting a pointer, no need to use aucmd_prepbuf() curbuf = (buf_T *)from; curwin->w_buffer = curbuf; - varp = get_varp(p); + varp = get_varp_scope(p, OPT_LOCAL); curbuf = save_curbuf; curwin->w_buffer = curbuf; } @@ -4878,7 +5030,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o win_T *save_curwin = curwin; curwin = (win_T *)from; curbuf = curwin->w_buffer; - varp = get_varp(p); + varp = get_varp_scope(p, OPT_LOCAL); curwin = save_curwin; curbuf = curwin->w_buffer; } @@ -6779,7 +6931,7 @@ static void langmap_set(void) ga_clear(&langmap_mapga); // clear the previous map first langmap_init(); // back to one-to-one map - for (p = p_langmap; p[0] != NUL; ) { + for (p = p_langmap; p[0] != NUL;) { for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';'; MB_PTR_ADV(p2)) { if (p2[0] == '\\' && p2[1] != NUL) { @@ -7746,7 +7898,7 @@ void set_fileformat(int eol_style, int opt_flags) } // This may cause the buffer to become (un)modified. - check_status(curbuf); + redraw_buf_status_later(curbuf); redraw_tabline = true; need_maketitle = true; // Set window title later. } diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 727b10f7a7..e9f44d2775 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -1048,7 +1048,7 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst } if (buf != NULL && buf->b_help) { - const size_t dlen = xstrlcpy((char *)dst, (char *)path_tail(src), dstlen); + const size_t dlen = STRLCPY(dst, path_tail(src), dstlen); return MIN(dlen, dstlen - 1); } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 3ff13c2b3f..24c7678633 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -21,7 +21,6 @@ #include "nvim/assert.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" @@ -369,7 +368,7 @@ static bool is_executable_in_path(const char *name, char **abspath) // is an executable file. char *p = path; bool rv = false; - for (;; ) { + for (;;) { char *e = xstrchrnul(p, ENV_SEPCHAR); // Combine the $PATH segment with `name`. diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 5b231f205b..3790eba212 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -17,7 +17,6 @@ #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/os/input.h" #include "nvim/state.h" @@ -183,6 +182,40 @@ void os_breakcheck(void) updating_screen = save_us; } +#define BREAKCHECK_SKIP 1000 +static int breakcheck_count = 0; + +/// Check for CTRL-C pressed, but only once in a while. +/// +/// Should be used instead of os_breakcheck() for functions that check for +/// each line in the file. Calling os_breakcheck() each time takes too much +/// time, because it will use system calls to check for input. +void line_breakcheck(void) +{ + if (++breakcheck_count >= BREAKCHECK_SKIP) { + breakcheck_count = 0; + os_breakcheck(); + } +} + +/// Like line_breakcheck() but check 10 times less often. +void fast_breakcheck(void) +{ + if (++breakcheck_count >= BREAKCHECK_SKIP * 10) { + breakcheck_count = 0; + os_breakcheck(); + } +} + +/// Like line_breakcheck() but check 100 times less often. +void veryfast_breakcheck(void) +{ + if (++breakcheck_count >= BREAKCHECK_SKIP * 100) { + breakcheck_count = 0; + os_breakcheck(); + } +} + /// Test whether a file descriptor refers to a terminal. /// diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 8049b3b80e..dce4b0c187 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -13,6 +13,10 @@ # include "nvim/os/unix_defs.h" #endif +#if !defined(NAME_MAX) && defined(_XOPEN_NAME_MAX) +#define NAME_MAX _XOPEN_NAME_MAX +#endif + #define BASENAMELEN (NAME_MAX - 5) // Use the system path length if it makes sense. diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 24ecf5c24f..3459646bad 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -15,6 +15,12 @@ # include <libutil.h> #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) # include <util.h> +#elif defined(__sun) +# include <sys/stream.h> +# include <sys/syscall.h> +# include <fcntl.h> +# include <unistd.h> +# include <signal.h> #else # include <pty.h> #endif @@ -38,6 +44,118 @@ # include "os/pty_process_unix.c.generated.h" #endif +#if defined(__sun) && !defined(HAVE_FORKPTY) + +// this header defines STR, just as nvim.h, but it is defined as ('S'<<8), +// to avoid #undef STR, #undef STR, #define STR ('S'<<8) just delay the +// inclusion of the header even though it gets include out of order. +#include <sys/stropts.h> + +static int openpty(int *amaster, int *aslave, char *name, + struct termios *termp, struct winsize *winp) +{ + int slave = -1; + int master = open("/dev/ptmx", O_RDWR); + if (master == -1) { + goto error; + } + + // grantpt will invoke a setuid program to change permissions + // and might fail if SIGCHLD handler is set, temporarily reset + // while running + void(*sig_saved)(int) = signal(SIGCHLD, SIG_DFL); + int res = grantpt(master); + signal(SIGCHLD, sig_saved); + + if (res == -1 || unlockpt(master) == -1) { + goto error; + } + + char *slave_name = ptsname(master); + if (slave_name == NULL) { + goto error; + } + + slave = open(slave_name, O_RDWR|O_NOCTTY); + if (slave == -1) { + goto error; + } + + // ptem emulates a terminal when used on a pseudo terminal driver, + // must be pushed before ldterm + ioctl(slave, I_PUSH, "ptem"); + // ldterm provides most of the termio terminal interface + ioctl(slave, I_PUSH, "ldterm"); + // ttcompat compatability with older terminal ioctls + ioctl(slave, I_PUSH, "ttcompat"); + + if (termp) { + tcsetattr(slave, TCSAFLUSH, termp); + } + if (winp) { + ioctl(slave, TIOCSWINSZ, winp); + } + + *amaster = master; + *aslave = slave; + // ignoring name, not passed and size is unknown in the API + + return 0; + +error: + if (slave != -1) { + close(slave); + } + if (master != -1) { + close(master); + } + return -1; +} + +static int login_tty(int fd) +{ + setsid(); + if (ioctl(fd, TIOCSCTTY, NULL) == -1) { + return -1; + } + + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) { + close(fd); + } + + return 0; +} + +static pid_t forkpty(int *amaster, char *name, + struct termios *termp, struct winsize *winp) +{ + int master, slave; + if (openpty(&master, &slave, name, termp, winp) == -1) { + return -1; + } + + pid_t pid = fork(); + switch (pid) { + case -1: + close(master); + close(slave); + return -1; + case 0: + close(master); + login_tty(slave); + return 0; + default: + close(slave); + *amaster = master; + return pid; + } +} + +#endif + /// termios saved at startup (for TUI) or initialized by pty_process_spawn(). static struct termios termios_default; @@ -198,7 +316,9 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL termios->c_cflag = CS8|CREAD; termios->c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK; - cfsetspeed(termios, 38400); + // not using cfsetspeed, not available on all platforms + cfsetispeed(termios, 38400); + cfsetospeed(termios, 38400); #ifdef IUTF8 termios->c_iflag |= IUTF8; diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 6ef0aa1091..e618b2788b 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -12,6 +12,7 @@ #include "nvim/event/libuv_process.h" #include "nvim/event/loop.h" #include "nvim/event/rstream.h" +#include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/fileio.h" #include "nvim/lib/kvec.h" @@ -20,7 +21,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option_defs.h" #include "nvim/os/shell.h" #include "nvim/os/signal.h" @@ -28,6 +28,7 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/types.h" +#include "nvim/tag.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -681,6 +682,116 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args) return exitcode; } +/// os_call_shell() wrapper. Handles 'verbose', :profile, and v:shell_error. +/// Invalidates cached tags. +/// +/// @return shell command exit code +int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) +{ + int retval; + proftime_T wait_time; + + if (p_verbose > 3) { + verbose_enter(); + smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd); + msg_putchar('\n'); + verbose_leave(); + } + + if (do_profiling == PROF_YES) { + prof_child_enter(&wait_time); + } + + if (*p_sh == NUL) { + emsg(_(e_shellempty)); + retval = -1; + } else { + // The external command may update a tags file, clear cached tags. + tag_freematch(); + + retval = os_call_shell(cmd, opts, extra_shell_arg); + } + + set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T)retval); + if (do_profiling == PROF_YES) { + prof_child_exit(&wait_time); + } + + return retval; +} + +/// Get the stdout of an external command. +/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not +/// NULL store the length there. +/// +/// @param cmd command to execute +/// @param infile optional input file name +/// @param flags can be kShellOptSilent or 0 +/// @param ret_len length of the stdout +/// +/// @return an allocated string, or NULL for error. +char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len) +{ + char_u *buffer = NULL; + + if (check_secure()) { + return NULL; + } + + // get a name for the temp file + char_u *tempname = vim_tempname(); + if (tempname == NULL) { + emsg(_(e_notmp)); + return NULL; + } + + // Add the redirection stuff + char_u *command = make_filter_cmd(cmd, infile, tempname); + + // Call the shell to execute the command (errors are ignored). + // Don't check timestamps here. + no_check_timestamps++; + call_shell(command, kShellOptDoOut | kShellOptExpand | flags, NULL); + no_check_timestamps--; + + xfree(command); + + // read the names from the file into memory + FILE *fd = os_fopen((char *)tempname, READBIN); + + if (fd == NULL) { + semsg(_(e_notopen), tempname); + goto done; + } + + fseek(fd, 0L, SEEK_END); + size_t len = (size_t)ftell(fd); // get size of temp file + fseek(fd, 0L, SEEK_SET); + + buffer = xmalloc(len + 1); + size_t i = fread((char *)buffer, 1, len, fd); + fclose(fd); + os_remove((char *)tempname); + if (i != len) { + semsg(_(e_notread), tempname); + XFREE_CLEAR(buffer); + } else if (ret_len == NULL) { + // Change NUL into SOH, otherwise the string is truncated. + for (i = 0; i < len; i++) { + if (buffer[i] == NUL) { + buffer[i] = 1; + } + } + + buffer[len] = NUL; // make sure the buffer is terminated + } else { + *ret_len = len; + } + +done: + xfree(tempname); + return buffer; +} /// os_system - synchronously execute a command in the shell /// /// example: diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index 0d125ec964..a8bf68a1a2 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -18,7 +18,6 @@ #include "nvim/main.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/os/signal.h" #include "nvim/vim.h" diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 10b0d391bf..5b824d23f4 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -146,7 +146,7 @@ char *stdpaths_user_data_subpath(const char *fname, const size_t trailing_pathse const size_t numcommas = (escape_commas ? memcnt(ret, ',', len) : 0); if (numcommas || trailing_pathseps) { ret = xrealloc(ret, len + trailing_pathseps + numcommas + 1); - for (size_t i = 0 ; i < len + numcommas ; i++) { + for (size_t i = 0; i < len + numcommas; i++) { if (ret[i] == ',') { memmove(ret + i + 1, ret + i, len - i + numcommas); ret[i] = '\\'; diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 9952e2b387..e0ce3fec31 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -18,6 +18,9 @@ # include <lm.h> #endif +// All user names (for ~user completion as done by shell). +static garray_T ga_users = GA_EMPTY_INIT_VALUE; + // Add a user name to the list of users in garray_T *users. // Do nothing if user name is NULL or empty. static void add_user(garray_T *users, char *user, bool need_copy) @@ -157,3 +160,60 @@ char *os_get_user_directory(const char *name) return NULL; } + +#if defined(EXITFREE) + +void free_users(void) +{ + ga_clear_strings(&ga_users); +} + +#endif + +/// Find all user names for user completion. +/// +/// Done only once and then cached. +static void init_users(void) +{ + static int lazy_init_done = false; + + if (lazy_init_done) { + return; + } + + lazy_init_done = true; + + os_get_usernames(&ga_users); +} + +/// Given to ExpandGeneric() to obtain an user names. +char_u *get_users(expand_T *xp, int idx) +{ + init_users(); + if (idx < ga_users.ga_len) { + return ((char_u **)ga_users.ga_data)[idx]; + } + return NULL; +} + +/// Check whether name matches a user name. +/// +/// @return 0 if name does not match any user name. +/// 1 if name partially matches the beginning of a user name. +/// 2 is name fully matches a user name. +int match_user(char_u *name) +{ + int n = (int)STRLEN(name); + int result = 0; + + init_users(); + for (int i = 0; i < ga_users.ga_len; i++) { + if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) { + return 2; // full match + } + if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) { + result = 1; // partial match + } + } + return result; +} diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 9396a5896a..1398dba0e4 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -20,7 +20,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/os/input.h" diff --git a/src/nvim/path.c b/src/nvim/path.c index 1085f7a10c..674d67e21a 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -18,7 +18,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" @@ -65,7 +64,7 @@ FileComparison path_full_compare(char_u *const s1, char_u *const s2, const bool if (expandenv) { expand_env(s1, exp1, MAXPATHL); } else { - xstrlcpy((char *)exp1, (const char *)s1, MAXPATHL); + STRLCPY(exp1, s1, MAXPATHL); } bool id_ok_1 = os_fileid((char *)exp1, &file_id_1); bool id_ok_2 = os_fileid((char *)s2, &file_id_2); @@ -1079,7 +1078,7 @@ const char *gettail_dir(const char *const fname) const char *next_dir_end = fname; bool look_for_sep = true; - for (const char *p = fname; *p != NUL; ) { + for (const char *p = fname; *p != NUL;) { if (vim_ispathsep(*p)) { if (look_for_sep) { next_dir_end = p; @@ -1289,8 +1288,8 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil && !path_is_absolute(p) && !(p[0] == '.' && (vim_ispathsep(p[1]) - || (p[1] == '.' && - vim_ispathsep(p[2]))))) { + || (p[1] == '.' + && vim_ispathsep(p[2]))))) { /* :find completion where 'path' is used. * Recursiveness is OK here. */ recursive = false; @@ -1337,6 +1336,17 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL; } +/// Free the list of files returned by expand_wildcards() or other expansion functions. +void FreeWild(int count, char_u **files) +{ + if (count <= 0 || files == NULL) { + return; + } + while (count--) { + xfree(files[count]); + } + xfree(files); +} /* * Return TRUE if we can expand this backtick thing here. @@ -1505,7 +1515,7 @@ void simplify_filename(char_u *filename) if (vim_ispathsep(*p)) { relative = false; - do{ + do { ++p; } while (vim_ispathsep(*p)); @@ -1517,8 +1527,8 @@ void simplify_filename(char_u *filename) * or "p" is at the "start" of the (absolute or relative) path name. */ if (vim_ispathsep(*p)) { STRMOVE(p, p + 1); // remove duplicate "/" - } else if (p[0] == '.' && - (vim_ispathsep(p[1]) || p[1] == NUL)) { + } else if (p[0] == '.' + && (vim_ispathsep(p[1]) || p[1] == NUL)) { if (p == start && relative) { p += 1 + (p[1] != NUL); // keep single "." or leading "./" } else { @@ -2198,7 +2208,7 @@ int match_suffix(char_u *fname) size_t fnamelen = STRLEN(fname); size_t setsuflen = 0; - for (char_u *setsuf = p_su; *setsuf; ) { + for (char_u *setsuf = p_su; *setsuf;) { setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,"); if (setsuflen == 0) { char_u *tail = path_tail(fname); @@ -2245,11 +2255,17 @@ int path_full_dir_name(char *directory, char *buffer, size_t len) } if (os_chdir(directory) != SUCCESS) { - // Do not return immediately since we may be in the wrong directory. - retval = FAIL; - } - - if (retval == FAIL || os_dirname((char_u *)buffer, len) == FAIL) { + // Path does not exist (yet). For a full path fail, + // will use the path as-is. For a relative path use + // the current directory and append the file name. + if (path_is_absolute((const char_u *)directory)) { + // Do not return immediately since we may be in the wrong directory. + retval = FAIL; + } else { + xstrlcpy(buffer, old_dir, len); + append_path(buffer, directory, len); + } + } else if (os_dirname((char_u *)buffer, len) == FAIL) { // Do not return immediately since we are in the wrong directory. retval = FAIL; } @@ -2395,9 +2411,9 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize) if (dir_len + 1 > sizeof(NameBuff)) { continue; } - xstrlcpy((char *)NameBuff, dir, dir_len + 1); - xstrlcat((char *)NameBuff, PATHSEPSTR, sizeof(NameBuff)); - xstrlcat((char *)NameBuff, argv0, sizeof(NameBuff)); + STRLCPY(NameBuff, dir, dir_len + 1); + STRLCAT(NameBuff, PATHSEPSTR, sizeof(NameBuff)); + STRLCAT(NameBuff, argv0, sizeof(NameBuff)); if (os_can_exe((char *)NameBuff, NULL, false)) { xstrlcpy(buf, (char *)NameBuff, bufsize); return; diff --git a/src/nvim/po/check.vim b/src/nvim/po/check.vim index d55d4cfa4d..aca878f9d5 100644 --- a/src/nvim/po/check.vim +++ b/src/nvim/po/check.vim @@ -162,7 +162,10 @@ endwhile " Check that the file is well formed according to msgfmts understanding if executable("msgfmt") let filename = expand("%") - let a = system("msgfmt --statistics OLD_PO_FILE_INPUT=yes " . filename) + " Newer msgfmt does not take OLD_PO_FILE_INPUT argument, must be in + " environment. + let $OLD_PO_FILE_INPUT = 'yes' + let a = system("msgfmt --statistics " . filename) if v:shell_error != 0 let error = matchstr(a, filename.':\zs\d\+\ze:')+0 for line in split(a, '\n') | echomsg line | endfor diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 950d187ad5..32d0ebe8eb 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -27,7 +27,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" @@ -539,7 +538,7 @@ static size_t efm_regpat_bufsz(char_u *efm) size_t sz; sz = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); - for (int i = FMT_PATTERNS - 1; i >= 0; ) { + for (int i = FMT_PATTERNS - 1; i >= 0;) { sz += STRLEN(fmt_pat[i--].pattern); } #ifdef BACKSLASH_IN_FILENAME @@ -658,7 +657,8 @@ static int qf_get_next_str_line(qfstate_T *state) state->linebuf = IObuff; state->linelen = len; } - STRLCPY(state->linebuf, p_str, state->linelen + 1); + memcpy(state->linebuf, p_str, state->linelen); + state->linebuf[state->linelen] = '\0'; // Increment using len in order to discard the rest of the line if it // exceeds LINE_MAXLEN. @@ -1183,7 +1183,7 @@ static void qf_store_title(qf_list_T *qfl, const char_u *title) char_u *p = xmallocz(len); qfl->qf_title = p; - xstrlcpy((char *)p, (const char *)title, len + 1); + STRLCPY(p, title, len + 1); } } @@ -1402,7 +1402,7 @@ static int qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields) len = CMDBUFFSIZE - 5; } STRCPY(fields->pattern, "^\\V"); - xstrlcat((char *)fields->pattern, (char *)rmp->startp[midx], len + 4); + STRLCAT(fields->pattern, rmp->startp[midx], len + 4); fields->pattern[len + 3] = '\\'; fields->pattern[len + 4] = '$'; fields->pattern[len + 5] = NUL; @@ -1424,7 +1424,7 @@ static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) if (dsize > CMDBUFFSIZE) { dsize = CMDBUFFSIZE; } - xstrlcat((char *)fields->module, (char *)rmp->startp[midx], dsize); + STRLCAT(fields->module, rmp->startp[midx], dsize); return QF_OK; } @@ -2028,7 +2028,7 @@ void copy_loclist_stack(win_T *from, win_T *to) /// Get buffer number for file "directory/fname". /// Also sets the b_has_qf_entry flag. -static int qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname ) +static int qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname) { char_u *ptr = NULL; char_u *bufname; @@ -3231,7 +3231,7 @@ static void qf_msg(qf_info_T *qi, int which, char *lead) memset(buf + len, ' ', 34 - len); buf[34] = NUL; } - xstrlcat((char *)buf, title, IOSIZE); + STRLCAT(buf, title, IOSIZE); } trunc_string(buf, buf, Columns - 1, IOSIZE); msg((char *)buf); @@ -4380,7 +4380,7 @@ static char_u *get_mef_name(void) } // Keep trying until the name doesn't exist yet. - for (;; ) { + for (;;) { if (start == -1) { start = (int)os_get_pid(); } else { diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index bec3bc9648..45e580dbee 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -64,7 +64,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" +#include "nvim/os/input.h" #include "nvim/plines.h" #include "nvim/garray.h" #include "nvim/strings.h" @@ -1760,13 +1760,14 @@ static char_u *regpiece(int *flagp) break; } if (re_multi_type(peekchr()) != NOT_MULTI) { - /* Can't have a multi follow a multi. */ - if (peekchr() == Magic('*')) - sprintf((char *)IObuff, _("E61: Nested %s*"), - reg_magic >= MAGIC_ON ? "" : "\\"); - else - sprintf((char *)IObuff, _("E62: Nested %s%c"), - reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr())); + // Can't have a multi follow a multi. + if (peekchr() == Magic('*')) { + snprintf((char *)IObuff, IOSIZE, _("E61: Nested %s*"), + reg_magic >= MAGIC_ON ? "" : "\\"); + } else { + snprintf((char *)IObuff, IOSIZE, _("E62: Nested %s%c"), + reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr())); + } EMSG_RET_NULL((char *)IObuff); } @@ -1926,11 +1927,11 @@ static char_u *regatom(int *flagp) case Magic('{'): case Magic('*'): c = no_Magic(c); - sprintf((char *)IObuff, _("E64: %s%c follows nothing"), - (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL) - ? "" : "\\", c); + snprintf((char *)IObuff, IOSIZE, _("E64: %s%c follows nothing"), + (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL) + ? "" : "\\", c); EMSG_RET_NULL((char *)IObuff); - /* NOTREACHED */ + // NOTREACHED case Magic('~'): /* previous substitute pattern */ if (reg_prev_sub != NULL) { @@ -3152,8 +3153,8 @@ static int read_limits(long *minval, long *maxval) regparse++; // Allow either \{...} or \{...\} } if (*regparse != '}') { - sprintf((char *)IObuff, _("E554: Syntax error in %s{...}"), - reg_magic == MAGIC_ALL ? "" : "\\"); + snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"), + reg_magic == MAGIC_ALL ? "" : "\\"); EMSG_RET_FAIL((char *)IObuff); } @@ -7263,9 +7264,10 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags) if (f) { fprintf(f, "Syntax error in \"%s\"\n", expr); fclose(f); - } else + } else { semsg("(NFA) Could not open \"%s\" to write !!!", - BT_REGEXP_DEBUG_LOG_NAME); + BT_REGEXP_DEBUG_LOG_NAME); + } } #endif // If the NFA engine failed, try the backtracking engine. The NFA engine diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 5df5cc5975..eac1b4596e 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -16,23 +16,22 @@ #include "nvim/ascii.h" #include "nvim/garray.h" - -/* - * Logging of NFA engine. - * - * The NFA engine can write four log files: - * - Error log: Contains NFA engine's fatal errors. - * - Dump log: Contains compiled NFA state machine's information. - * - Run log: Contains information of matching procedure. - * - Debug log: Contains detailed information of matching procedure. Can be - * disabled by undefining NFA_REGEXP_DEBUG_LOG. - * The first one can also be used without debug mode. - * The last three are enabled when compiled as debug mode and individually - * disabled by commenting them out. - * The log files can get quite big! - * Do disable all of this when compiling Vim for debugging, undefine REGEXP_DEBUG in - * regexp.c - */ +#include "nvim/os/input.h" + +// Logging of NFA engine. +// +// The NFA engine can write four log files: +// - Error log: Contains NFA engine's fatal errors. +// - Dump log: Contains compiled NFA state machine's information. +// - Run log: Contains information of matching procedure. +// - Debug log: Contains detailed information of matching procedure. Can be +// disabled by undefining NFA_REGEXP_DEBUG_LOG. +// The first one can also be used without debug mode. +// The last three are enabled when compiled as debug mode and individually +// disabled by commenting them out. +// The log files can get quite big! +// To disable all of this when compiling Vim for debugging, undefine REGEXP_DEBUG in +// regexp.c #ifdef REGEXP_DEBUG # define NFA_REGEXP_ERROR_LOG "nfa_regexp_error.log" # define NFA_REGEXP_DUMP_LOG "nfa_regexp_dump.log" diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 674d807e96..1c04cb16b3 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -12,7 +12,6 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/lua/executor.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/runtime.h" @@ -479,7 +478,7 @@ RuntimeSearchPath runtime_search_path_build(void) CharVec after_path = KV_INITIAL_VALUE; static char_u buf[MAXPATHL]; - for (char *entry = (char *)p_pp; *entry != NUL; ) { + for (char *entry = (char *)p_pp; *entry != NUL;) { char *cur_entry = entry; copy_option_part((char_u **)&entry, buf, MAXPATHL, ","); @@ -491,7 +490,7 @@ RuntimeSearchPath runtime_search_path_build(void) char *rtp_entry; - for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL; ) { + for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL;) { char *cur_entry = rtp_entry; copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ","); size_t buflen = STRLEN(buf); @@ -663,7 +662,7 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack) } const char *insp = NULL; const char *after_insp = NULL; - for (const char *entry = (const char *)p_rtp; *entry != NUL; ) { + for (const char *entry = (const char *)p_rtp; *entry != NUL;) { const char *cur_entry = entry; copy_option_part((char_u **)&entry, buf, MAXPATHL, ","); @@ -872,7 +871,7 @@ static void add_pack_start_dir(char_u *fname, void *cookie) continue; } STRLCPY(buf, fname, MAXPATHL); - xstrlcat((char *)buf, start_pat[i], sizeof buf); + STRLCAT(buf, start_pat[i], sizeof buf); if (pack_has_entries(buf)) { add_pack_dir_to_rtp(buf, true); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 63e904079e..a938a3b062 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -98,7 +98,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" @@ -314,6 +313,19 @@ void update_curbuf(int type) update_screen(type); } +/// called when the status bars for the buffer 'buf' need to be updated +void redraw_buf_status_later(buf_T *buf) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer == buf && wp->w_status_height) { + wp->w_redr_status = true; + if (must_redraw < VALID) { + must_redraw = VALID; + } + } + } +} + /// Redraw the parts of the screen that is marked for redraw. /// /// Most code shouldn't call this directly, rather use redraw_later() and @@ -451,9 +463,11 @@ int update_screen(int type) // reset cmdline_row now (may have been changed temporarily) compute_cmdrow(); + bool hl_changed = false; // Check for changed highlighting if (need_highlight_changed) { highlight_changed(); + hl_changed = true; } if (type == CLEAR) { // first clear screen @@ -554,7 +568,7 @@ int update_screen(int type) * buffer. Each buffer must only be done once. */ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - update_window_hl(wp, type >= NOT_VALID); + update_window_hl(wp, type >= NOT_VALID || hl_changed); buf_T *buf = wp->w_buffer; if (buf->b_mod_set) { @@ -1084,7 +1098,7 @@ static void win_update(win_T *wp, Providers *providers) */ bot_start = 0; idx = 0; - for (;; ) { + for (;;) { wp->w_lines[idx] = wp->w_lines[j]; /* stop at line that didn't fit, unless it is still * valid (no lines deleted) */ @@ -1359,7 +1373,7 @@ static void win_update(win_T *wp, Providers *providers) win_check_ns_hl(wp); - for (;; ) { + for (;;) { /* stop updating when reached the end of the window (check for _past_ * the end of the window is at the end of the loop) */ if (row == wp->w_grid.Rows) { @@ -1508,7 +1522,7 @@ static void win_update(win_T *wp, Providers *providers) int x = row + new_rows; // move entries in w_lines[] upwards - for (;; ) { + for (;;) { // stop at last valid entry in w_lines[] if (i >= wp->w_lines_valid) { wp->w_lines_valid = j; @@ -1692,7 +1706,7 @@ static void win_update(win_T *wp, Providers *providers) if (eof) { // we hit the end of the file wp->w_botline = buf->b_ml.ml_line_count + 1; j = win_get_fill(wp, wp->w_botline); - if (j > 0 && !wp->w_botfill) { + if (j > 0 && !wp->w_botfill && row < wp->w_grid.Rows) { // Display filler text below last line. win_line() will check // for ml_line_count+1 and only draw filler lines foldinfo_T info = FOLDINFO_INIT; @@ -2707,7 +2721,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc int sign_idx = 0; // Repeat for the whole displayed line. - for (;; ) { + for (;;) { int has_match_conc = 0; ///< match wants to conceal bool did_decrement_ptr = false; @@ -2744,7 +2758,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc p_extra = p_extra_free; c_extra = NUL; c_final = NUL; - char_attr = win_hl_attr(wp, HLF_FC); + if (use_cursor_line_sign(wp, lnum)) { + char_attr = win_hl_attr(wp, HLF_CLF); + } else { + char_attr = win_hl_attr(wp, HLF_FC); + } } } @@ -2755,7 +2773,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc * buffer or when using Netbeans. */ int count = win_signcol_count(wp); if (count > 0) { - get_sign_display_info(false, wp, sattrs, row, + get_sign_display_info(false, wp, lnum, sattrs, row, startrow, filler_lines, filler_todo, count, &c_extra, &c_final, extra, sizeof(extra), &p_extra, &n_extra, @@ -2776,7 +2794,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) { int count = win_signcol_count(wp); - get_sign_display_info(true, wp, sattrs, row, + get_sign_display_info(true, wp, lnum, sattrs, row, startrow, filler_lines, filler_todo, count, &c_extra, &c_final, extra, sizeof(extra), &p_extra, &n_extra, @@ -3117,7 +3135,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // the match. if (cur != NULL && shl != &search_hl - && syn_name2id((char_u *)"Conceal") == cur->hlg_id) { + && syn_name2id("Conceal") == cur->hlg_id) { has_match_conc = v == (long)shl->startcol ? 2 : 1; match_conc = cur->conceal_char; } else { @@ -4413,8 +4431,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc || filler_todo > 0 || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && p_extra != at_end_str) - || (n_extra != 0 && - (c_extra != NUL || *p_extra != NUL)))) { + || (n_extra != 0 + && (c_extra != NUL || *p_extra != NUL)))) { bool wrap = wp->w_p_wrap // Wrapping enabled. && filler_todo <= 0 // Not drawing diff filler lines. && lcs_eol_one != -1 // Haven't printed the lcs_eol character. @@ -4615,6 +4633,14 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) } } +// Return true if CursorLineSign highlight is to be used. +static bool use_cursor_line_sign(win_T *wp, linenr_T lnum) +{ + return wp->w_p_cul + && lnum == wp->w_cursor.lnum + && (wp->w_p_culopt_flags & CULOPT_NBR); +} + // Get information needed to display the sign in line 'lnum' in window 'wp'. // If 'nrcol' is TRUE, the sign is going to be displayed in the number column. // Otherwise the sign is going to be displayed in the sign column. @@ -4622,11 +4648,11 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) // @param count max number of signs // @param[out] n_extrap number of characters from pp_extra to display // @param[in, out] sign_idxp Index of the displayed sign -static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[], int row, - int startrow, int filler_lines, int filler_todo, int count, - int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size, - char_u **pp_extra, int *n_extrap, int *char_attrp, - int *draw_statep, int *sign_idxp) +static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[], + int row, int startrow, int filler_lines, int filler_todo, + int count, int *c_extrap, int *c_finalp, char_u *extra, + size_t extra_size, char_u **pp_extra, int *n_extrap, + int *char_attrp, int *draw_statep, int *sign_idxp) { // Draw cells with the sign value or blank. *c_extrap = ' '; @@ -4634,7 +4660,11 @@ static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[], if (nrcol) { *n_extrap = number_width(wp) + 1; } else { - *char_attrp = win_hl_attr(wp, HLF_SC); + if (use_cursor_line_sign(wp, lnum)) { + *char_attrp = win_hl_attr(wp, HLF_CLS); + } else { + *char_attrp = win_hl_attr(wp, HLF_SC); + } *n_extrap = win_signcol_width(wp); } @@ -4674,7 +4704,12 @@ static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[], (*pp_extra)[*n_extrap] = NUL; } } - *char_attrp = sattr->sat_texthl; + + if (use_cursor_line_sign(wp, lnum) && sattr->sat_culhl > 0) { + *char_attrp = sattr->sat_culhl; + } else { + *char_attrp = sattr->sat_texthl; + } } } @@ -5011,8 +5046,8 @@ static int skip_status_match_char(expand_T *xp, char_u *s) if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP) || ((xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES) - && (s[0] == '\t' || - (s[0] == '\\' && s[1] != NUL)))) { + && (s[0] == '\t' + || (s[0] == '\\' && s[1] != NUL)))) { #ifndef BACKSLASH_IN_FILENAME if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') { return 2; @@ -6144,7 +6179,7 @@ static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T minc * or none is found in this line. */ called_emsg = FALSE; - for (;; ) { + for (;;) { // Stop searching after passing the time limit. if (profile_passed_limit(shl->tm)) { shl->lnum = 0; // no match found in time @@ -6930,7 +6965,7 @@ int showmode(void) do_mode = ((p_smd && msg_silent == 0) && ((State & TERM_FOCUS) || (State & INSERT) - || restart_edit + || restart_edit != NUL || VIsual_active)); if (do_mode || reg_recording != 0) { // Don't show mode right now, when not redrawing or inside a mapping. @@ -7010,7 +7045,7 @@ int showmode(void) } msg_puts_attr(_(" INSERT"), attr); } else if (restart_edit == 'I' || restart_edit == 'i' - || restart_edit == 'a') { + || restart_edit == 'a' || restart_edit == 'A') { msg_puts_attr(_(" (insert)"), attr); } else if (restart_edit == 'R') { msg_puts_attr(_(" (replace)"), attr); diff --git a/src/nvim/search.c b/src/nvim/search.c index 0756fbf37d..906c9a6f47 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -14,6 +14,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/change.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -31,11 +32,11 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/os/input.h" #include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/regexp.h" @@ -370,8 +371,8 @@ int ignorecase_opt(char_u *pat, int ic_in, int scs) { int ic = ic_in; if (ic && !no_smartcase && scs - && !(ctrl_x_mode_not_default() && - curbuf->b_p_inf)) { + && !(ctrl_x_mode_not_default() + && curbuf->b_p_inf)) { ic = !pat_has_uppercase(pat); } no_smartcase = false; @@ -757,7 +758,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, * relative to the end of the match. */ match_ok = false; - for (;; ) { + for (;;) { // Remember a position that is before the start // position, we use it if it's the last match in // the line. Always accept a position after @@ -1105,7 +1106,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, /* * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar". */ - for (;; ) { + for (;;) { bool show_top_bot_msg = false; searchstr = pat; @@ -1353,6 +1354,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, } retval = 1; // pattern found + if (sia && sia->sa_wrapped) { + apply_autocmds(EVENT_SEARCHWRAPPED, NULL, NULL, false, NULL); + } + /* * Add character and/or line offset */ @@ -1465,7 +1470,7 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat) if (buf->b_ml.ml_line_count == 0) { return FAIL; } - for (;; ) { + for (;;) { pos->lnum += dir; if (pos->lnum < 1) { if (p_ws) { @@ -1585,7 +1590,7 @@ int searchc(cmdarg_T *cap, int t_cmd) len = (int)STRLEN(p); while (count--) { - for (;; ) { + for (;;) { if (dir > 0) { col += utfc_ptr2len(p + col); if (col >= len) { @@ -1808,6 +1813,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) initc = NUL; } else if (initc != '#' && initc != NUL) { find_mps_values(&initc, &findc, &backwards, true); + if (dir) { + backwards = (dir == FORWARD) ? false : true; + } if (findc == NUL) { return NULL; } @@ -1870,7 +1878,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) if (linep[pos.col] == NUL && pos.col) { --pos.col; } - for (;; ) { + for (;;) { initc = utf_ptr2char(linep + pos.col); if (initc == NUL) { break; @@ -1894,7 +1902,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) // Set "match_escaped" if there are an odd number of // backslashes. - for (col = pos.col; check_prevcol(linep, col, '\\', &col); ) { + for (col = pos.col; check_prevcol(linep, col, '\\', &col);) { bslcnt++; } match_escaped = (bslcnt & 1); @@ -2278,7 +2286,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) int col, bslcnt = 0; if (!cpo_bsl) { - for (col = pos.col; check_prevcol(linep, col, '\\', &col); ) { + for (col = pos.col; check_prevcol(linep, col, '\\', &col);) { bslcnt++; } } @@ -2520,7 +2528,7 @@ int findsent(Direction dir, long count) const int startlnum = pos.lnum; const bool cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL; - for (;; ) { // find end of sentence + for (;;) { // find end of sentence c = gchar_pos(&pos); if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) { if (dir == BACKWARD && pos.lnum != startlnum) { @@ -2530,7 +2538,7 @@ int findsent(Direction dir, long count) } if (c == '.' || c == '!' || c == '?') { tpos = pos; - do{ + do { if ((c = inc(&tpos)) == -1) { break; } @@ -3004,7 +3012,7 @@ static void back_in_line(void) int sclass; // starting class sclass = cls(); - for (;; ) { + for (;;) { if (curwin->w_cursor.col == 0) { // stop at start of line break; } @@ -3427,12 +3435,22 @@ int current_block(oparg_T *oap, long count, int include, int what, int other) // user wants. save_cpo = p_cpo; p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%"); - while (count-- > 0) { - if ((pos = findmatch(NULL, what)) == NULL) { - break; + if ((pos = findmatch(NULL, what)) != NULL) { + while (count-- > 0) { + if ((pos = findmatch(NULL, what)) == NULL) { + break; + } + curwin->w_cursor = *pos; + start_pos = *pos; // the findmatch for end_pos will overwrite *pos + } + } else { + while (count-- > 0) { + if ((pos = findmatchlimit(NULL, what, FM_FORWARD, 0)) == NULL) { + break; + } + curwin->w_cursor = *pos; + start_pos = *pos; // the findmatch for end_pos will overwrite *pos } - curwin->w_cursor = *pos; - start_pos = *pos; // the findmatch for end_pos will overwrite *pos } p_cpo = save_cpo; @@ -3527,7 +3545,7 @@ static bool in_html_tag(bool end_tag) int lc = NUL; pos_T pos; - for (p = line + curwin->w_cursor.col; p > line; ) { + for (p = line + curwin->w_cursor.col; p > line;) { if (*p == '<') { // find '<' under/before cursor break; } @@ -3555,7 +3573,7 @@ static bool in_html_tag(bool end_tag) } // check that the matching '>' is not preceded by '/' - for (;; ) { + for (;;) { if (inc(&pos) < 0) { return false; } @@ -3791,7 +3809,7 @@ extend: } else { dir = FORWARD; } - for (i = count; --i >= 0; ) { + for (i = count; --i >= 0;) { if (start_lnum == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) { retval = FAIL; @@ -3806,7 +3824,7 @@ extend: start_lnum -= dir; break; } - for (;; ) { + for (;;) { if (start_lnum == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) { break; @@ -3946,7 +3964,7 @@ static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape) { int c; - for (;; ) { + for (;;) { c = line[col]; if (c == NUL) { return -1; @@ -4118,7 +4136,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar) // Also do this when there is a Visual area, a' may leave the cursor // in between two strings. col_start = 0; - for (;; ) { + for (;;) { // Find open quote character. col_start = find_next_quote(line, col_start, quotechar, NULL); if (col_start < 0 || col_start > first_col) { @@ -4842,7 +4860,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo } line = ml_get(lnum); - for (;; ) { + for (;;) { if (incl_regmatch.regprog != NULL && vim_regexec(&incl_regmatch, line, (colnr_T)0)) { char_u *p_fname = (curr_fname == curbuf->b_fname) @@ -5235,6 +5253,9 @@ search_line: if (depth == -1) { // match in current file if (l_g_do_tagpreview != 0) { + if (!win_valid(curwin_save)) { + break; + } if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL, NULL, true, lnum, false))) { break; // failed to jump to file @@ -5379,7 +5400,7 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action, if (got_int) { // 'q' typed at "--more--" message return; } - for (;; ) { + for (;;) { p = line + STRLEN(line) - 1; if (fp != NULL) { // We used fgets(), so get rid of newline at end diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 89f9d3a719..e75a244031 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -3977,7 +3977,7 @@ static bool shada_removable(const char *name) bool retval = false; char *new_name = (char *)home_replace_save(NULL, (char_u *)name); - for (p = (char *)p_shada; *p; ) { + for (p = (char *)p_shada; *p;) { (void)copy_option_part((char_u **)&p, (char_u *)part, ARRAY_SIZE(part), ", "); if (part[0] == 'r') { home_replace(NULL, (char_u *)(part + 1), (char_u *)NameBuff, MAXPATHL, true); diff --git a/src/nvim/sign.c b/src/nvim/sign.c index dfa863d0ff..32be714184 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -31,6 +31,7 @@ struct sign { char_u *sn_text; // text used instead of pixmap int sn_line_hl; // highlight ID for line int sn_text_hl; // highlight ID for text + int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set int sn_num_hl; // highlight ID for line number }; @@ -80,7 +81,7 @@ static signgroup_T *sign_group_ref(const char_u *groupname) hi = hash_lookup(&sg_table, (char *)groupname, STRLEN(groupname), hash); if (HASHITEM_EMPTY(hi)) { // new group - group = xmalloc((unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); + group = xmalloc(sizeof(signgroup_T) + STRLEN(groupname)); STRCPY(group->sg_name, groupname); group->sg_refcount = 1; @@ -499,6 +500,9 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[]) if (sp->sn_line_hl != 0) { sattr.sat_linehl = syn_id2attr(sp->sn_line_hl); } + if (sp->sn_cul_hl != 0) { + sattr.sat_culhl = syn_id2attr(sp->sn_cul_hl); + } if (sp->sn_num_hl != 0) { sattr.sat_numhl = syn_id2attr(sp->sn_num_hl); } @@ -774,7 +778,7 @@ static int sign_cmd_idx(char_u *begin_cmd, char_u *end_cmd) char_u save = *end_cmd; *end_cmd = (char_u)NUL; - for (idx = 0; ; idx++) { + for (idx = 0;; idx++) { if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { break; } @@ -816,7 +820,7 @@ static sign_T *alloc_new_sign(char_u *name) // Check that next_sign_typenr is not already being used. // This only happens after wrapping around. Hopefully // another one got deleted and we can use its number. - for (lp = first_sign; lp != NULL; ) { + for (lp = first_sign; lp != NULL;) { if (lp->sn_typenr == next_sign_typenr) { next_sign_typenr++; if (next_sign_typenr == MAX_TYPENR) { @@ -901,7 +905,7 @@ static int sign_define_init_text(sign_T *sp, char_u *text) /// Define a new sign or update an existing sign int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl, - char *numhl) + char_u *culhl, char *numhl) { sign_T *sp_prev; sign_T *sp; @@ -939,15 +943,35 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text } if (linehl != NULL) { - sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl)); + if (*linehl == NUL) { + sp->sn_line_hl = 0; + } else { + sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl)); + } } if (texthl != NULL) { - sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl)); + if (*texthl == NUL) { + sp->sn_text_hl = 0; + } else { + sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl)); + } + } + + if (culhl != NULL) { + if (*culhl == NUL) { + sp->sn_cul_hl = 0; + } else { + sp->sn_cul_hl = syn_check_group((char *)culhl, (int)STRLEN(culhl)); + } } if (numhl != NULL) { - sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + if (*numhl == NUL) { + sp->sn_num_hl = 0; + } else { + sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + } } return OK; @@ -1133,6 +1157,7 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) char_u *text = NULL; char_u *linehl = NULL; char_u *texthl = NULL; + char_u *culhl = NULL; char_u *numhl = NULL; int failed = false; @@ -1155,6 +1180,9 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) } else if (STRNCMP(arg, "texthl=", 7) == 0) { arg += 7; texthl = vim_strnsave(arg, (size_t)(p - arg)); + } else if (STRNCMP(arg, "culhl=", 6) == 0) { + arg += 6; + culhl = vim_strnsave(arg, (size_t)(p - arg)); } else if (STRNCMP(arg, "numhl=", 6) == 0) { arg += 6; numhl = vim_strnsave(arg, (size_t)(p - arg)); @@ -1166,13 +1194,14 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) } if (!failed) { - sign_define_by_name(sign_name, icon, linehl, text, texthl, (char *)numhl); + sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, (char *)numhl); } xfree(icon); xfree(text); xfree(linehl); xfree(texthl); + xfree(culhl); xfree(numhl); } @@ -1481,6 +1510,13 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict) } tv_dict_add_str(retdict, S_LEN("texthl"), (char *)p); } + if (sp->sn_cul_hl > 0) { + p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, false); + if (p == NULL) { + p = "NONE"; + } + tv_dict_add_str(retdict, S_LEN("culhl"), (char *)p); + } if (sp->sn_num_hl > 0) { p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, false); if (p == NULL) { @@ -1609,6 +1645,16 @@ static void sign_list_defined(sign_T *sp) msg_puts(p); } } + if (sp->sn_cul_hl > 0) { + msg_puts(" culhl="); + const char *const p = get_highlight_name_ext(NULL, + sp->sn_cul_hl - 1, false); + if (p == NULL) { + msg_puts("NONE"); + } else { + msg_puts(p); + } + } if (sp->sn_num_hl > 0) { msg_puts(" numhl="); const char *const p = get_highlight_name_ext(NULL, @@ -1847,6 +1893,7 @@ int sign_define_from_dict(const char *name_arg, dict_T *dict) char *linehl = NULL; char *text = NULL; char *texthl = NULL; + char *culhl = NULL; char *numhl = NULL; int retval = -1; @@ -1866,11 +1913,12 @@ int sign_define_from_dict(const char *name_arg, dict_T *dict) linehl = tv_dict_get_string(dict, "linehl", true); text = tv_dict_get_string(dict, "text", true); texthl = tv_dict_get_string(dict, "texthl", true); + culhl = tv_dict_get_string(dict, "culhl", true); numhl = tv_dict_get_string(dict, "numhl", true); } if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl, - (char_u *)text, (char_u *)texthl, numhl) + (char_u *)text, (char_u *)texthl, (char_u *)culhl, numhl) == OK) { retval = 0; } @@ -1881,6 +1929,7 @@ cleanup: xfree(linehl); xfree(text); xfree(texthl); + xfree(culhl); xfree(numhl); return retval; diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index 46436b2c8e..c734502878 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -38,6 +38,7 @@ typedef struct sign_attrs_S { char_u *sat_text; int sat_texthl; int sat_linehl; + int sat_culhl; int sat_numhl; } sign_attrs_T; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 20081bce4f..bd31e98faa 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -94,12 +94,12 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/hashtab.h" +#include "nvim/input.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/normal.h" #include "nvim/option.h" #include "nvim/os/input.h" @@ -622,7 +622,7 @@ static void find_word(matchinf_T *mip, int mode) // - there is a byte that doesn't match, // - we reach the end of the tree, // - or we reach the end of the line. - for (;; ) { + for (;;) { if (flen <= 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); } @@ -689,7 +689,7 @@ static void find_word(matchinf_T *mip, int mode) // One space in the good word may stand for several spaces in the // checked word. if (c == ' ') { - for (;; ) { + for (;;) { if (flen <= 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); } @@ -1269,7 +1269,7 @@ static void find_prefix(matchinf_T *mip, int mode) // - there is a byte that doesn't match, // - we reach the end of the tree, // - or we reach the end of the line. - for (;; ) { + for (;;) { if (flen == 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); } @@ -2083,7 +2083,7 @@ char *did_set_spelllang(win_T *wp) wp->w_s->b_cjk = 0; // Loop over comma separated language names. - for (splp = spl_copy; *splp != NUL; ) { + for (splp = spl_copy; *splp != NUL;) { // Get one language name. copy_option_part(&splp, lang, MAXWLEN, ","); region = NULL; @@ -2354,7 +2354,7 @@ static void use_midword(slang_T *lp, win_T *wp) return; } - for (char_u *p = lp->sl_midword; *p != NUL; ) { + for (char_u *p = lp->sl_midword; *p != NUL;) { const int c = utf_ptr2char(p); const int l = utfc_ptr2len(p); if (c < 256 && l <= 2) { @@ -2759,7 +2759,7 @@ int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int bufle int outi = 0; // Fold one character at a time. - for (char_u *p = str; p < str + len; ) { + for (char_u *p = str; p < str + len;) { if (outi + MB_MAXBYTES > buflen) { buf[outi] = NUL; return FAIL; @@ -2806,7 +2806,7 @@ int spell_check_sps(void) sps_flags = 0; sps_limit = 9999; - for (p = p_sps; *p != NUL; ) { + for (p = p_sps; *p != NUL;) { copy_option_part(&p, buf, MAXPATHL, ","); f = 0; @@ -3118,7 +3118,7 @@ static bool check_need_cap(linenr_T lnum, colnr_T col) regmatch.regprog = curwin->w_s->b_cap_prog; regmatch.rm_ic = FALSE; p = line + endcol; - for (;; ) { + for (;;) { MB_PTR_BACK(line, p); if (p == line || spell_iswordp_nmw(p, curwin)) { break; @@ -3330,7 +3330,7 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma sps_copy = vim_strsave(p_sps); // Loop over the items in 'spellsuggest'. - for (p = sps_copy; *p != NUL; ) { + for (p = sps_copy; *p != NUL;) { copy_option_part(&p, buf, MAXPATHL, ","); if (STRNCMP(buf, "expr:", 5) == 0) { @@ -3557,7 +3557,7 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper) static void allcap_copy(char_u *word, char_u *wcopy) { char_u *d = wcopy; - for (char_u *s = word; *s != NUL; ) { + for (char_u *s = word; *s != NUL;) { int c = mb_cptr2char_adv((const char_u **)&s); if (c == 0xdf) { @@ -4352,8 +4352,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // just deleted this byte, accepting it is always cheaper than // delete + substitute. if (c == fword[sp->ts_fidx] - || (sp->ts_tcharlen > 0 && - sp->ts_isdiff != DIFF_NONE)) { + || (sp->ts_tcharlen > 0 + && sp->ts_isdiff != DIFF_NONE)) { newscore = 0; } else { newscore = SCORE_SUBST; @@ -4513,7 +4513,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // skip over NUL bytes n = sp->ts_arridx; - for (;; ) { + for (;;) { if (sp->ts_curi > byts[n]) { // Only NUL bytes at this node, go to next state. PROF_STORE(sp->ts_state) @@ -5257,7 +5257,7 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u * // space. if (ascii_iswhite(su->su_badptr[su->su_badlen]) && *skiptowhite(stp->st_word) == NUL) { - for (p = fword; *(p = skiptowhite(p)) != NUL; ) { + for (p = fword; *(p = skiptowhite(p)) != NUL;) { STRMOVE(p, p + 1); } } @@ -5568,7 +5568,7 @@ static int soundfold_find(slang_T *slang, char_u *word) byts = slang->sl_sbyts; idxs = slang->sl_sidxs; - for (;; ) { + for (;;) { // First byte is the number of possible bytes. len = byts[arridx++]; @@ -5701,7 +5701,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword, // "thee the" is added next to changing the first "the" the "thee". const char_u *pgood = goodword + STRLEN(goodword); char_u *pbad = su->su_badptr + badlenarg; - for (;; ) { + for (;;) { goodlen = (int)(pgood - goodword); badlen = (int)(pbad - su->su_badptr); if (goodlen <= 0 || badlen <= 0) { @@ -6004,7 +6004,7 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // The sl_sal_first[] table contains the translation for chars up to // 255, sl_sal the rest. - for (char_u *s = inword; *s != NUL; ) { + for (char_u *s = inword; *s != NUL;) { int c = mb_cptr2char_adv((const char_u **)&s); if (utf_class(c) == 0) { c = ' '; @@ -6015,7 +6015,7 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) if (ip == NULL) { // empty list, can't match c = NUL; } else { - for (;; ) { // find "c" in the list + for (;;) { // find "c" in the list if (*ip == 0) { // not found c = NUL; break; @@ -6069,7 +6069,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // Remove accents, if wanted. We actually remove all non-word characters. // But keep white space. wordlen = 0; - for (const char_u *s = inword; *s != NUL; ) { + for (const char_u *s = inword; *s != NUL;) { const char_u *t = s; c = mb_cptr2char_adv(&s); if (slang->sl_rem_accents) { @@ -6591,12 +6591,12 @@ static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) // Get the characters from the multi-byte strings and put them in an // int array for easy access. badlen = 0; - for (const char_u *p = badword; *p != NUL; ) { + for (const char_u *p = badword; *p != NUL;) { wbadword[badlen++] = mb_cptr2char_adv(&p); } wbadword[badlen++] = 0; goodlen = 0; - for (const char_u *p = goodword; *p != NUL; ) { + for (const char_u *p = goodword; *p != NUL;) { wgoodword[goodlen++] = mb_cptr2char_adv(&p); } wgoodword[goodlen++] = 0; @@ -6690,12 +6690,12 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo // Get the characters from the multi-byte strings and put them in an // int array for easy access. bi = 0; - for (const char_u *p = badword; *p != NUL; ) { + for (const char_u *p = badword; *p != NUL;) { wbadword[bi++] = mb_cptr2char_adv(&p); } wbadword[bi++] = 0; gi = 0; - for (const char_u *p = goodword; *p != NUL; ) { + for (const char_u *p = goodword; *p != NUL;) { wgoodword[gi++] = mb_cptr2char_adv(&p); } wgoodword[gi++] = 0; @@ -6713,9 +6713,9 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo score = 0; minscore = limit + 1; - for (;; ) { + for (;;) { // Skip over an equal part, score remains the same. - for (;; ) { + for (;;) { bc = wbadword[bi]; gc = wgoodword[gi]; @@ -7289,7 +7289,7 @@ int spell_word_start(int startcol) // Find a word character before "startcol". line = get_cursor_line_ptr(); - for (p = line + startcol; p > line; ) { + for (p = line + startcol; p > line;) { MB_PTR_BACK(line, p); if (spell_iswordp_nmw(p, curwin)) { break; diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 0fc9012f27..42bb3c61a5 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -237,8 +237,8 @@ #include "nvim/fileio.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/option.h" +#include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/path.h" #include "nvim/regexp.h" @@ -549,7 +549,7 @@ static inline int spell_check_magic_string(FILE *const fd) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { char buf[VIMSPELLMAGICL]; - SPELL_READ_BYTES(buf, VIMSPELLMAGICL, fd, ; ); + SPELL_READ_BYTES(buf, VIMSPELLMAGICL, fd,; ); if (memcmp(buf, VIMSPELLMAGIC, VIMSPELLMAGICL) != 0) { return SP_FORMERROR; } @@ -640,7 +640,7 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile // <SECTIONS>: <section> ... <sectionend> // <section>: <sectionID> <sectionflags> <sectionlen> (section contents) - for (;; ) { + for (;;) { n = getc(fd); // <sectionID> or <sectionend> if (n == SN_END) { break; @@ -960,7 +960,7 @@ someerror: ga_init(&ga, 1, 100); for (wordnr = 0; wordnr < wcount; ++wordnr) { ga.ga_len = 0; - for (;; ) { + for (;;) { c = getc(fd); // <sugline> if (c < 0) { goto someerror; @@ -1030,7 +1030,7 @@ static int read_region_section(FILE *fd, slang_T *lp, int len) if (len > MAXREGIONS * 2) { return SP_FORMERROR; } - SPELL_READ_NONNUL_BYTES((char *)lp->sl_regions, (size_t)len, fd, ; ); + SPELL_READ_NONNUL_BYTES((char *)lp->sl_regions, (size_t)len, fd,; ); lp->sl_regions[len] = NUL; return 0; } @@ -1097,7 +1097,7 @@ static int read_prefcond_section(FILE *fd, slang_T *lp) if (n > 0) { char buf[MAXWLEN + 1]; buf[0] = '^'; // always match at one position only - SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd, ; ); + SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd,; ); buf[n + 1] = NUL; lp->sl_prefprog[i] = vim_regcomp((char_u *)buf, RE_MAGIC | RE_STRING); } @@ -1548,7 +1548,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // First count the number of items for each list. Temporarily use // sl_sal_first[] for this. - for (p = from, s = to; *p != NUL && *s != NUL; ) { + for (p = from, s = to; *p != NUL && *s != NUL;) { const int c = mb_cptr2char_adv((const char_u **)&p); MB_CPTR_ADV(s); if (c >= 256) { @@ -1571,7 +1571,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // Put the characters up to 255 in sl_sal_first[] the rest in a sl_sal // list. memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); - for (p = from, s = to; *p != NUL && *s != NUL; ) { + for (p = from, s = to; *p != NUL && *s != NUL;) { const int c = mb_cptr2char_adv((const char_u **)&p); const int i = mb_cptr2char_adv((const char_u **)&s); if (c >= 256) { @@ -1647,7 +1647,7 @@ static int *mb_str2wide(char_u *s) int i = 0; int *res = xmalloc((mb_charlen(s) + 1) * sizeof(int)); - for (char_u *p = s; *p != NUL; ) { + for (char_u *p = s; *p != NUL;) { res[i++] = mb_ptr2char_adv((const char_u **)&p); } res[i] = NUL; @@ -2095,7 +2095,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // Split the line up in white separated items. Put a NUL after each // item. itemcnt = 0; - for (p = line;; ) { + for (p = line;;) { while (*p != NUL && *p <= ' ') { // skip white space and CR/NL ++p; } @@ -2646,7 +2646,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) int c; // Check that every character appears only once. - for (p = items[1]; *p != NUL; ) { + for (p = items[1]; *p != NUL;) { c = mb_ptr2char_adv((const char_u **)&p); if ((!GA_EMPTY(&spin->si_map) && vim_strchr(spin->si_map.ga_data, c) @@ -2804,7 +2804,7 @@ static void aff_process_flags(afffile_T *affile, affentry_T *entry) if (entry->ae_flags != NULL && (affile->af_compforbid != 0 || affile->af_comppermit != 0)) { - for (p = entry->ae_flags; *p != NUL; ) { + for (p = entry->ae_flags; *p != NUL;) { prevp = p; flag = get_affitem(affile->af_flagtype, &p); if (flag == affile->af_comppermit || flag == affile->af_compforbid) { @@ -2922,7 +2922,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla spin->si_compflags = p; tp = p + STRLEN(p); - for (p = compflags; *p != NUL; ) { + for (p = compflags; *p != NUL;) { if (vim_strchr((char_u *)"/?*+[]", *p) != NULL) { // Copy non-flag characters directly. *tp++ = *p++; @@ -2985,7 +2985,7 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) case AFT_CAPLONG: case AFT_LONG: - for (p = afflist; *p != NUL; ) { + for (p = afflist; *p != NUL;) { n = mb_ptr2char_adv((const char_u **)&p); if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) && *p != NUL) { @@ -2998,7 +2998,7 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) break; case AFT_NUM: - for (p = afflist; *p != NUL; ) { + for (p = afflist; *p != NUL;) { int digits = getdigits_int(&p, true, 0); assert(digits >= 0); n = (unsigned int)digits; @@ -3248,9 +3248,9 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) // Extract flags from the affix list. flags |= get_affix_flags(affile, afflist); - if (affile->af_needaffix != 0 && - flag_in_afflist(affile->af_flagtype, afflist, - affile->af_needaffix)) { + if (affile->af_needaffix != 0 + && flag_in_afflist(affile->af_flagtype, afflist, + affile->af_needaffix)) { need_affix = true; } @@ -3311,32 +3311,32 @@ static int get_affix_flags(afffile_T *affile, char_u *afflist) { int flags = 0; - if (affile->af_keepcase != 0 && - flag_in_afflist(affile->af_flagtype, afflist, - affile->af_keepcase)) { + if (affile->af_keepcase != 0 + && flag_in_afflist(affile->af_flagtype, afflist, + affile->af_keepcase)) { flags |= WF_KEEPCAP | WF_FIXCAP; } - if (affile->af_rare != 0 && - flag_in_afflist(affile->af_flagtype, afflist, affile->af_rare)) { + if (affile->af_rare != 0 + && flag_in_afflist(affile->af_flagtype, afflist, affile->af_rare)) { flags |= WF_RARE; } - if (affile->af_bad != 0 && - flag_in_afflist(affile->af_flagtype, afflist, affile->af_bad)) { + if (affile->af_bad != 0 + && flag_in_afflist(affile->af_flagtype, afflist, affile->af_bad)) { flags |= WF_BANNED; } - if (affile->af_needcomp != 0 && - flag_in_afflist(affile->af_flagtype, afflist, - affile->af_needcomp)) { + if (affile->af_needcomp != 0 + && flag_in_afflist(affile->af_flagtype, afflist, + affile->af_needcomp)) { flags |= WF_NEEDCOMP; } - if (affile->af_comproot != 0 && - flag_in_afflist(affile->af_flagtype, afflist, - affile->af_comproot)) { + if (affile->af_comproot != 0 + && flag_in_afflist(affile->af_flagtype, afflist, + affile->af_comproot)) { flags |= WF_COMPROOT; } - if (affile->af_nosuggest != 0 && - flag_in_afflist(affile->af_flagtype, afflist, - affile->af_nosuggest)) { + if (affile->af_nosuggest != 0 + && flag_in_afflist(affile->af_flagtype, afflist, + affile->af_nosuggest)) { flags |= WF_NOSUGGEST; } return flags; @@ -3355,7 +3355,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist char_u key[AH_KEY_LEN]; hashitem_T *hi; - for (p = afflist; *p != NUL; ) { + for (p = afflist; *p != NUL;) { prevp = p; if (get_affitem(affile->af_flagtype, &p) != 0) { // A flag is a postponed prefix flag if it appears in "af_pref" @@ -3389,7 +3389,7 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl char_u key[AH_KEY_LEN]; hashitem_T *hi; - for (p = afflist; *p != NUL; ) { + for (p = afflist; *p != NUL;) { prevp = p; if (get_affitem(affile->af_flagtype, &p) != 0) { // A flag is a compound flag if it appears in "af_comp". @@ -4446,10 +4446,10 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) putc(SN_PREFCOND, fd); // <sectionID> putc(SNF_REQUIRED, fd); // <sectionflags> - size_t l = (size_t)write_spell_prefcond(NULL, &spin->si_prefcond); + size_t l = (size_t)write_spell_prefcond(NULL, &spin->si_prefcond, &fwv); put_bytes(fd, l, 4); // <sectionlen> - write_spell_prefcond(fd, &spin->si_prefcond); + write_spell_prefcond(fd, &spin->si_prefcond, &fwv); } // SN_REP: <repcount> <rep> ... @@ -5793,7 +5793,7 @@ static int set_spell_finish(spelltab_T *new_st) // Write the table with prefix conditions to the .spl file. // When "fd" is NULL only count the length of what is written. -static int write_spell_prefcond(FILE *fd, garray_T *gap) +static int write_spell_prefcond(FILE *fd, garray_T *gap, size_t *fwv) { assert(gap->ga_len >= 0); @@ -5801,8 +5801,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap) put_bytes(fd, (uintmax_t)gap->ga_len, 2); // <prefcondcnt> } size_t totlen = 2 + (size_t)gap->ga_len; // <prefcondcnt> and <condlen> bytes - size_t x = 1; // collect return value of fwrite() - for (int i = 0; i < gap->ga_len; ++i) { + for (int i = 0; i < gap->ga_len; i++) { // <prefcond> : <condlen> <condstr> char_u *p = ((char_u **)gap->ga_data)[i]; if (p != NULL) { @@ -5810,7 +5809,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap) if (fd != NULL) { assert(len <= INT_MAX); fputc((int)len, fd); - x &= fwrite(p, len, 1, fd); + *fwv &= fwrite(p, len, 1, fd); } totlen += len; } else if (fd != NULL) { @@ -5845,7 +5844,7 @@ static void set_map_str(slang_T *lp, char_u *map) // The similar characters are stored separated with slashes: // "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and // before the same slash. For characters above 255 sl_map_hash is used. - for (p = map; *p != NUL; ) { + for (p = map; *p != NUL;) { c = mb_cptr2char_adv((const char_u **)&p); if (c == '/') { headc = 0; diff --git a/src/nvim/state.c b/src/nvim/state.c index 4eb0073873..68bc76660d 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -4,7 +4,9 @@ #include <assert.h> #include "nvim/ascii.h" +#include "nvim/autocmd.h" #include "nvim/edit.h" +#include "nvim/eval.h" #include "nvim/ex_docmd.h" #include "nvim/getchar.h" #include "nvim/lib/kvec.h" @@ -136,7 +138,7 @@ int get_real_state(void) /// @returns[allocated] mode string char *get_mode(void) { - char *buf = xcalloc(4, sizeof(char)); + char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char)); if (VIsual_active) { if (VIsual_select) { @@ -202,3 +204,33 @@ char *get_mode(void) return buf; } + +/// Fires a ModeChanged autocmd. +void trigger_modechanged(void) +{ + if (!has_event(EVENT_MODECHANGED)) { + return; + } + + char *mode = get_mode(); + if (STRCMP(mode, last_mode) == 0) { + xfree(mode); + return; + } + + save_v_event_T save_v_event; + dict_T *v_event = get_v_event(&save_v_event); + tv_dict_add_str(v_event, S_LEN("new_mode"), mode); + tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode); + + char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":"); + char_u *pat = concat_str(pat_pre, (char_u *)mode); + xfree(pat_pre); + + apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf); + xfree(last_mode); + last_mode = mode; + + xfree(pat); + restore_v_event(v_event, &save_v_event); +} diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 47cbf01996..e2a8108c45 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -31,7 +31,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" @@ -242,7 +241,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n #endif *d++ = '\''; - for (const char_u *p = string; *p != NUL; ) { + for (const char_u *p = string; *p != NUL;) { #ifdef WIN32 if (!p_ssl) { if (*p == '"') { @@ -394,6 +393,18 @@ void del_trailing_spaces(char_u *ptr) } } +#if !defined(HAVE_STRNLEN) +size_t xstrnlen(const char *s, size_t n) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + const char *end = memchr(s, '\0', n); + if (end == NULL) { + return n; + } + return end - s; +} +#endif + #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) /* * Compare two strings, ignoring case, using current locale. @@ -405,7 +416,7 @@ int vim_stricmp(const char *s1, const char *s2) { int i; - for (;; ) { + for (;;) { i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2); if (i != 0) { return i; // this character different diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 504d1cd16e..a9447165c2 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -32,8 +32,8 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" +#include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/os_unix.h" @@ -115,6 +115,8 @@ static int include_none = 0; // when 1 include "nvim/None" static int include_default = 0; // when 1 include "nvim/default" static int include_link = 0; // when 2 include "nvim/link" and "clear" +#define MAX_SYN_NAME 200 + /// The "term", "cterm" and "gui" arguments can be any combination of the /// following names, separated by commas (but no spaces!). static char *(hl_name_table[]) = @@ -691,7 +693,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) * Restrict the search for the end of a comment to b_syn_sync_maxlines. */ if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL) { - for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; ) { + for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) { if (SYN_ITEMS(syn_block)[idx].sp_syn.id == syn_block->b_syn_sync_id && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START) { @@ -752,7 +754,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum) { syn_start_line(); - for (;; ) { + for (;;) { had_sync_point = syn_finish_line(true); // When a sync point has been found, remember where, and // continue to look for another one, further on in the line. @@ -1146,7 +1148,7 @@ static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf) linenr_T n; prev = NULL; - for (p = block->b_sst_first; p != NULL; ) { + for (p = block->b_sst_first; p != NULL;) { if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top) { n = p->sst_lnum + buf->b_mod_xlines; if (n <= buf->b_mod_bot) { @@ -1449,7 +1451,7 @@ static bool syn_stack_equal(synstate_T *sp) } int i; - for (i = current_state.ga_len; --i >= 0; ) { + for (i = current_state.ga_len; --i >= 0;) { // If the item has another index the state is different. if (bp[i].bs_idx != CUR_STATE(i).si_idx) { break; @@ -1788,9 +1790,9 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con line = syn_getcurline(); const char_u *cur_pos = line + current_col; if (vim_iswordp_buf(cur_pos, syn_buf) - && (current_col == 0 || - !vim_iswordp_buf(cur_pos - 1 - utf_head_off(line, cur_pos - 1), - syn_buf))) { + && (current_col == 0 + || !vim_iswordp_buf(cur_pos - 1 - utf_head_off(line, cur_pos - 1), + syn_buf))) { syn_id = check_keyword_id(line, (int)current_col, &endcol, &flags, &next_list, cur_si, &cchar); if (syn_id != 0) { @@ -1852,7 +1854,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con */ next_match_idx = 0; // no match in this line yet next_match_col = MAXCOL; - for (int idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; ) { + for (int idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) { synpat_T *const spp = &(SYN_ITEMS(syn_block)[idx]); if (spp->sp_syncing == syncing && (displaying || !(spp->sp_flags & HL_DISPLAY)) @@ -2207,7 +2209,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con /// @return true if we already matched pattern "idx" at the current column. static bool did_match_already(int idx, garray_T *gap) { - for (int i = current_state.ga_len; --i >= 0; ) { + for (int i = current_state.ga_len; --i >= 0;) { if (CUR_STATE(i).si_m_startcol == (int)current_col && CUR_STATE(i).si_m_lnum == (int)current_lnum && CUR_STATE(i).si_idx == idx) { @@ -2217,7 +2219,7 @@ static bool did_match_already(int idx, garray_T *gap) // Zero-width matches with a nextgroup argument are not put on the syntax // stack, and can only be matched once anyway. - for (int i = gap->ga_len; --i >= 0; ) { + for (int i = gap->ga_len; --i >= 0;) { if (((int *)(gap->ga_data))[i] == idx) { return true; } @@ -2318,7 +2320,7 @@ static void check_state_ends(void) int had_extend; cur_si = &CUR_STATE(current_state.ga_len - 1); - for (;; ) { + for (;;) { if (cur_si->si_ends && (cur_si->si_m_endpos.lnum < current_lnum || (cur_si->si_m_endpos.lnum == current_lnum @@ -2658,7 +2660,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ /* * Find the SKIP or first END pattern after the last START pattern. */ - for (;; ) { + for (;;) { spp = &(SYN_ITEMS(syn_block)[idx]); if (spp->sp_type != SPTYPE_START) { break; @@ -2687,7 +2689,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ // use syntax iskeyword option save_chartab(buf_chartab); - for (;; ) { + for (;;) { /* * Find end pattern that matches first after "matchcol". */ @@ -3286,13 +3288,13 @@ void syntax_clear(synblock_T *block) clear_keywtab(&block->b_keywtab_ic); // free the syntax patterns - for (int i = block->b_syn_patterns.ga_len; --i >= 0; ) { + for (int i = block->b_syn_patterns.ga_len; --i >= 0;) { syn_clear_pattern(block, i); } ga_clear(&block->b_syn_patterns); // free the syntax clusters - for (int i = block->b_syn_clusters.ga_len; --i >= 0; ) { + for (int i = block->b_syn_clusters.ga_len; --i >= 0;) { syn_clear_cluster(block, i); } ga_clear(&block->b_syn_clusters); @@ -3336,7 +3338,7 @@ void reset_synblock(win_T *wp) static void syntax_sync_clear(void) { // free the syntax patterns - for (int i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) { + for (int i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0;) { if (SYN_ITEMS(curwin->w_s)[i].sp_syncing) { syn_remove_pattern(curwin->w_s, i); } @@ -3484,7 +3486,7 @@ static void syn_clear_one(const int id, const bool syncing) } // clear the patterns for "id" - for (int idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; ) { + for (int idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0;) { spp = &(SYN_ITEMS(curwin->w_s)[idx]); if (spp->sp_syn.id != id || spp->sp_syncing != syncing) { continue; @@ -3874,7 +3876,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const msg_putchar(c); // output the pattern, in between a char that is not in the pattern - for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; ) { + for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL;) { if (sepchars[++i] == NUL) { i = 0; // no good char found, just use the first one break; @@ -4015,7 +4017,7 @@ static void syn_clear_keyword(int id, hashtab_T *ht) } --todo; kp_prev = NULL; - for (kp = HI2KE(hi); kp != NULL; ) { + for (kp = HI2KE(hi); kp != NULL;) { if (kp->k_syn.id == id) { kp_next = kp->ke_next; if (kp_prev == NULL) { @@ -4191,7 +4193,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha opt->flags |= HL_CONCEAL; } - for (;; ) { + for (;;) { /* * This is used very often when a large number of keywords is defined. * Need to skip quickly when no option name is found. @@ -4201,7 +4203,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha break; } - for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0; ) { + for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0;) { p = flagtab[fidx].name; int i; for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) { @@ -4271,9 +4273,9 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha if (STRCMP(gname, "NONE") == 0) { *opt->sync_idx = NONE_IDX; } else { - syn_id = syn_name2id(gname); + syn_id = syn_name2id((char *)gname); int i; - for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) { + for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0;) { if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START) { *opt->sync_idx = i; @@ -4460,7 +4462,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) // 2: Add an entry for each keyword. for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) { - for (p = vim_strchr(kw, '[');; ) { + for (p = vim_strchr(kw, '[');;) { if (p != NULL) { *p = NUL; } @@ -4972,7 +4974,7 @@ static int syn_scl_name2id(char_u *name) // Avoid using stricmp() too much, it's slow on some systems char_u *name_u = vim_strsave_up(name); int i; - for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; ) { + for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0;) { if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0) { break; @@ -5078,7 +5080,7 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing) } scl_id -= SYNID_CLUSTER; - for (;; ) { + for (;;) { if (STRNICMP(rest, "add", 3) == 0 && (ascii_iswhite(rest[3]) || rest[3] == '=')) { opt_len = 3; @@ -5174,7 +5176,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) */ ++end; do { - for (idx = SPO_COUNT; --idx >= 0; ) { + for (idx = SPO_COUNT; --idx >= 0;) { if (STRNCMP(end, spo_name_tab[idx], 3) == 0) { break; } @@ -5264,7 +5266,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) } next_arg = skipwhite(arg_end); } else if (!eap->skip) { - curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment"); + curwin->w_s->b_syn_sync_id = syn_name2id("Comment"); } } else if (STRNCMP(key, "LINES", 5) == 0 || STRNCMP(key, "MINLINES", 8) == 0 @@ -5401,7 +5403,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis do { for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) { } - char_u *const name = xmalloc((int)(end - p + 3)); // leave room for "^$" + char_u *const name = xmalloc(end - p + 3); // leave room for "^$" STRLCPY(name + 1, p, end - p + 1); if (STRCMP(name + 1, "ALLBUT") == 0 || STRCMP(name + 1, "ALL") == 0 @@ -5456,7 +5458,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis regmatch.rm_ic = TRUE; id = 0; - for (int i = highlight_ga.ga_len; --i >= 0; ) { + for (int i = highlight_ga.ga_len; --i >= 0;) { if (vim_regexec(®match, HL_TABLE()[i].sg_name, (colnr_T)0)) { if (round == 2) { // Got more items than expected; can happen @@ -6175,6 +6177,8 @@ static const char *highlight_init_both[] = { "default link LineNrAbove LineNr", "default link LineNrBelow LineNr", "default link QuickFixLine Search", + "default link CursorLineSign SignColumn", + "default link CursorLineFold FoldColumn", "default link Substitute Search", "default link Whitespace NonText", "default link MsgSeparator StatusLine", @@ -6520,7 +6524,7 @@ const char *const highlight_init_cmdline[] = { /// Create default links for Nvim* highlight groups used for cmdline coloring void syn_init_cmdline_highlight(bool reset, bool init) { - for (size_t i = 0 ; highlight_init_cmdline[i] != NULL ; i++) { + for (size_t i = 0; highlight_init_cmdline[i] != NULL; i++) { do_highlight(highlight_init_cmdline[i], reset, init); } } @@ -6968,7 +6972,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) attr = 0; off = 0; while (arg[off] != NUL) { - for (i = ARRAY_SIZE(hl_attr_table); --i >= 0; ) { + for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) { len = (int)STRLEN(hl_name_table[i]); if (STRNICMP(arg + off, hl_name_table[i], len) == 0) { attr |= hl_attr_table[i]; @@ -7040,7 +7044,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } else { // Reduce calls to STRICMP a bit, it can be slow. off = TOUPPER_ASC(*arg); - for (i = ARRAY_SIZE(color_names); --i >= 0; ) { + for (i = ARRAY_SIZE(color_names); --i >= 0;) { if (off == color_names[i][0] && STRICMP(arg + 1, color_names[i] + 1) == 0) { break; @@ -7403,9 +7407,9 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg for (int i = 0; hl_attr_table[i] != 0; i++) { if (iarg & hl_attr_table[i]) { if (buf[0] != NUL) { - xstrlcat((char *)buf, ",", 100); + xstrlcat(buf, ",", 100); } - xstrlcat((char *)buf, hl_name_table[i], 100); + xstrlcat(buf, hl_name_table[i], 100); iarg &= ~hl_attr_table[i]; // don't want "inverse" } } @@ -7536,6 +7540,7 @@ static bool syn_list_header(const bool did_header, const int outlen, const int i { int endcol = 19; bool newline = true; + int name_col = 0; bool adjust = true; if (!did_header) { @@ -7544,6 +7549,7 @@ static bool syn_list_header(const bool did_header, const int outlen, const int i return true; } msg_outtrans(HL_TABLE()[id - 1].sg_name); + name_col = msg_col; endcol = 15; } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { msg_putchar(' '); @@ -7570,6 +7576,9 @@ static bool syn_list_header(const bool did_header, const int outlen, const int i // Show "xxx" with the attributes. if (!did_header) { + if (endcol == Columns - 1 && endcol <= name_col) { + msg_putchar(' '); + } msg_puts_attr("xxx", syn_id2attr(id)); msg_putchar(' '); } @@ -7605,10 +7614,10 @@ static void set_hl_attr(int idx) } } -int syn_name2id(const char_u *name) +int syn_name2id(const char *name) FUNC_ATTR_NONNULL_ALL { - return syn_name2id_len(name, STRLEN(name)); + return syn_name2id_len((char_u *)name, STRLEN(name)); } /// Lookup a highlight group name and return its ID. @@ -7618,10 +7627,9 @@ int syn_name2id(const char_u *name) int syn_name2id_len(const char_u *name, size_t len) FUNC_ATTR_NONNULL_ALL { - char name_u[201]; + char name_u[MAX_SYN_NAME + 1]; - if (len == 0 || len > 200) { - // ID names over 200 chars don't deserve to be found! + if (len == 0 || len > MAX_SYN_NAME) { return 0; } @@ -7641,7 +7649,7 @@ int syn_name2id_len(const char_u *name, size_t len) int syn_name2attr(const char_u *name) FUNC_ATTR_NONNULL_ALL { - int id = syn_name2id(name); + int id = syn_name2id((char *)name); if (id != 0) { return syn_id2attr(id); @@ -7652,7 +7660,7 @@ int syn_name2attr(const char_u *name) /* * Return TRUE if highlight group "name" exists. */ -int highlight_exists(const char_u *name) +int highlight_exists(const char *name) { return syn_name2id(name) > 0; } @@ -7679,6 +7687,10 @@ char_u *syn_id2name(int id) /// @return 0 for failure else the id of the group int syn_check_group(const char *name, int len) { + if (len > MAX_SYN_NAME) { + emsg(_(e_highlight_group_name_too_long)); + return 0; + } int id = syn_name2id_len((char_u *)name, len); if (id == 0) { // doesn't exist yet return syn_add_group(vim_strnsave((char_u *)name, len)); @@ -7784,7 +7796,7 @@ int syn_get_final_id(int hl_id) * Follow links until there is no more. * Look out for loops! Break after 100 links. */ - for (count = 100; --count >= 0; ) { + for (count = 100; --count >= 0;) { struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one // ACHTUNG: when using "tmp" attribute (no link) the function might be @@ -7870,7 +7882,7 @@ static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int void highlight_changed(void) { int id; - char_u userhl[30]; // use 30 to avoid compiler warning + char userhl[30]; // use 30 to avoid compiler warning int id_S = -1; int id_SNC = 0; int hlcnt; @@ -7919,7 +7931,7 @@ void highlight_changed(void) id_S = hlcnt + 10; } for (int i = 0; i < 9; i++) { - sprintf((char *)userhl, "User%d", i + 1); + snprintf(userhl, sizeof(userhl), "User%d", i + 1); id = syn_name2id(userhl); if (id == 0) { highlight_user[i] = 0; @@ -7983,10 +7995,10 @@ static void highlight_list(void) { int i; - for (i = 10; --i >= 0; ) { + for (i = 10; --i >= 0;) { highlight_list_two(i, HL_ATTR(HLF_D)); } - for (i = 40; --i >= 0; ) { + for (i = 40; --i >= 0;) { highlight_list_two(99, 0); } } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 673ebc2668..a10a2a0c32 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -25,11 +25,11 @@ #include "nvim/fold.h" #include "nvim/garray.h" #include "nvim/if_cscope.h" +#include "nvim/input.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os/input.h" @@ -141,12 +141,12 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc /// type == DT_LTAG: use location list for displaying tag matches /// type == DT_FREE: free cached matches /// -/// for cscope, returns TRUE if we jumped to tag or aborted, FALSE otherwise +/// for cscope, returns true if we jumped to tag or aborted, false otherwise /// /// @param tag tag (pattern) to jump to /// @param forceit :ta with ! /// @param verbose print "tag not found" message -int do_tag(char_u *tag, int type, int count, int forceit, int verbose) +bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -163,7 +163,7 @@ int do_tag(char_u *tag, int type, int count, int forceit, int verbose) int error_cur_match = 0; int save_pos = false; fmark_T saved_fmark; - int jumped_to_tag = false; + bool jumped_to_tag = false; int new_num_matches; char_u **new_matches; int use_tagstack; @@ -431,7 +431,7 @@ int do_tag(char_u *tag, int type, int count, int forceit, int verbose) /* * Repeat searching for tags, when a file has not been found. */ - for (;; ) { + for (;;) { int other_name; char_u *name; @@ -1638,7 +1638,7 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int /* * Read and parse the lines in the file one by one */ - for (;; ) { + for (;;) { // check for CTRL-C typed, more often when jumping around if (state == TS_BINARY || state == TS_SKIP_BACK) { line_breakcheck(); @@ -2403,7 +2403,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf) * tnp->tn_did_filefind_init == FALSE: setup for next part in 'tags'. * tnp->tn_did_filefind_init == TRUE: find next file in this part. */ - for (;; ) { + for (;;) { if (tnp->tn_did_filefind_init) { fname = vim_findfile(tnp->tn_search_ctx); if (fname != NULL) { @@ -2687,7 +2687,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) // copy the command to pbuf[], remove trailing CR/NL str = tagp.command; - for (pbuf_end = pbuf; *str && *str != '\n' && *str != '\r'; ) { + for (pbuf_end = pbuf; *str && *str != '\n' && *str != '\r';) { *pbuf_end++ = *str++; if (pbuf_end - pbuf + 1 >= LSIZE) { break; @@ -3056,7 +3056,7 @@ static int find_extra(char_u **pp) char_u first_char = **pp; // Repeat for addresses separated with ';' - for (;; ) { + for (;;) { if (ascii_isdigit(*str)) { str = skipdigits(str + 1); } else if (*str == '/' || *str == '?') { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 9002ac4967..d97c24dcf7 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -46,6 +46,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/change.h" +#include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/event/loop.h" #include "nvim/event/time.h" @@ -63,7 +64,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/option.h" @@ -324,10 +324,11 @@ void terminal_close(Terminal *term, int status) } if (buf && !is_autocmd_blocked()) { - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); tv_dict_add_nr(dict, S_LEN("status"), status); apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); } } @@ -393,7 +394,7 @@ void terminal_enter(void) long save_w_p_so = curwin->w_p_so; long save_w_p_siso = curwin->w_p_siso; if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) { - if (strcmp((char *)curwin->w_p_culopt, "number")) { + if (STRCMP(curwin->w_p_culopt, "number")) { save_w_p_culopt = curwin->w_p_culopt; curwin->w_p_culopt = (char_u *)xstrdup("number"); } @@ -412,6 +413,7 @@ void terminal_enter(void) curwin->w_redr_status = true; // For mode() in statusline. #8323 ui_busy_start(); apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf); + trigger_modechanged(); s->state.execute = terminal_execute; s->state.check = terminal_check; @@ -462,9 +464,7 @@ static void terminal_check_cursor(void) row_to_linenr(term, term->cursor.row)); // Nudge cursor when returning to normal-mode. int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); - curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off); - curwin->w_cursor.coladd = 0; - mb_check_adjust_col(curwin); + coladvance(MAX(0, term->cursor.col + off)); } // Function executed before each iteration of terminal mode. @@ -1466,6 +1466,17 @@ static void refresh_scrollback(Terminal *term, buf_T *buf) int width, height; vterm_get_size(term->vt, &height, &width); + // May still have pending scrollback after increase in terminal height if the + // scrollback wasn't refreshed in time; append these to the top of the buffer. + int row_offset = term->sb_pending; + while (term->sb_pending > 0 && buf->b_ml.ml_line_count < height) { + fetch_row(term, term->sb_pending - row_offset - 1, width); + ml_append(0, (uint8_t *)term->textbuf, 0, false); + appended_lines(0, 1); + term->sb_pending--; + } + + row_offset -= term->sb_pending; while (term->sb_pending > 0) { // This means that either the window height has decreased or the screen // became full and libvterm had to push all rows up. Convert the first @@ -1476,7 +1487,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf) ml_delete(1, false); deleted_lines(1, 1); } - fetch_row(term, -term->sb_pending, width); + fetch_row(term, -term->sb_pending - row_offset, width); int buf_index = (int)buf->b_ml.ml_line_count - height; ml_append(buf_index, (uint8_t *)term->textbuf, 0, false); appended_lines(buf_index, 1); @@ -1506,6 +1517,13 @@ static void refresh_screen(Terminal *term, buf_T *buf) // Terminal height may have decreased before `invalid_end` reflects it. term->invalid_end = MIN(term->invalid_end, height); + // There are no invalid rows. + if (term->invalid_start >= term->invalid_end) { + term->invalid_start = INT_MAX; + term->invalid_end = -1; + return; + } + for (int r = term->invalid_start, linenr = row_to_linenr(term, r); r < term->invalid_end; r++, linenr++) { fetch_row(term, r, width); diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 49993c03aa..ab047fd2a8 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -197,7 +197,12 @@ func RunTheTest(test) " Close any extra tab pages and windows and make the current one not modified. while tabpagenr('$') > 1 + let winid = win_getid() quit! + if winid == win_getid() + echoerr 'Could not quit window' + break + endif endwhile while 1 diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 8fd60d6a5a..164149476f 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -1,5 +1,9 @@ " Test argument list commands +func Reset_arglist() + args a | %argd +endfunc + func Test_argidx() args a b c last @@ -26,6 +30,8 @@ func Test_argidx() endfunc func Test_argadd() + call Reset_arglist() + %argdelete argadd a b c call assert_equal(0, argidx()) @@ -115,8 +121,7 @@ endfunc " Test for [count]argument and [count]argdelete commands " Ported from the test_argument_count.in test script func Test_argument() - " Clean the argument list - arga a | %argd + call Reset_arglist() let save_hidden = &hidden set hidden @@ -244,8 +249,7 @@ endfunc " Test for 0argadd and 0argedit " Ported from the test_argument_0count.in test script func Test_zero_argadd() - " Clean the argument list - arga a | %argd + call Reset_arglist() arga a b c d 2argu @@ -272,10 +276,6 @@ func Test_zero_argadd() call assert_equal('file with spaces', expand('%')) endfunc -func Reset_arglist() - args a | %argd -endfunc - " Test for argc() func Test_argc() call Reset_arglist() diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 0b76828dd7..9ad727241e 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -26,4 +26,42 @@ func Test_set_filename() call delete('samples/Xtest') endfunc +func Test_verbose_pwd() + CheckFunction test_autochdir + let cwd = getcwd() + call test_autochdir() + + edit global.txt + call assert_match('\[global\].*testdir$', execute('verbose pwd')) + + call mkdir('Xautodir') + split Xautodir/local.txt + lcd Xautodir + call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd')) + + set acd + wincmd w + call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) + execute 'lcd' cwd + call assert_match('\[window\].*testdir$', execute('verbose pwd')) + execute 'tcd' cwd + call assert_match('\[tabpage\].*testdir$', execute('verbose pwd')) + execute 'cd' cwd + call assert_match('\[global\].*testdir$', execute('verbose pwd')) + edit + call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) + wincmd w + call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) + set noacd + call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) + wincmd w + call assert_match('\[global\].*testdir', execute('verbose pwd')) + wincmd w + call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd')) + + bwipe! + call chdir(cwd) + call delete('Xautodir', 'rf') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index c350a17236..4e1a24af61 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -33,7 +33,7 @@ if has('timers') let g:triggered = 0 au CursorHoldI * let g:triggered += 1 set updatetime=20 - call timer_start(LoadAdjust(100), 'ExitInsertMode') + call timer_start(LoadAdjust(200), 'ExitInsertMode') call feedkeys('a', 'x!') call assert_equal(1, g:triggered) unlet g:triggered @@ -222,6 +222,7 @@ func Test_win_tab_autocmd() augroup testing au WinNew * call add(g:record, 'WinNew') + au WinClosed * call add(g:record, 'WinClosed') au WinEnter * call add(g:record, 'WinEnter') au WinLeave * call add(g:record, 'WinLeave') au TabNew * call add(g:record, 'TabNew') @@ -238,8 +239,8 @@ func Test_win_tab_autocmd() call assert_equal([ \ 'WinLeave', 'WinNew', 'WinEnter', \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', - \ 'WinLeave', 'TabLeave', 'TabClosed', 'WinEnter', 'TabEnter', - \ 'WinLeave', 'WinEnter' + \ 'WinLeave', 'TabLeave', 'WinClosed', 'TabClosed', 'WinEnter', 'TabEnter', + \ 'WinLeave', 'WinClosed', 'WinEnter' \ ], g:record) let g:record = [] @@ -250,7 +251,7 @@ func Test_win_tab_autocmd() call assert_equal([ \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', \ 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', - \ 'TabClosed' + \ 'WinClosed', 'TabClosed' \ ], g:record) augroup testing @@ -259,6 +260,45 @@ func Test_win_tab_autocmd() unlet g:record endfunc +func Test_WinClosed() + " Test that the pattern is matched against the closed window's ID, and both + " <amatch> and <afile> are set to it. + new + let winid = win_getid() + let g:matched = v:false + augroup test-WinClosed + autocmd! + execute 'autocmd WinClosed' winid 'let g:matched = v:true' + autocmd WinClosed * let g:amatch = str2nr(expand('<amatch>')) + autocmd WinClosed * let g:afile = str2nr(expand('<afile>')) + augroup END + close + call assert_true(g:matched) + call assert_equal(winid, g:amatch) + call assert_equal(winid, g:afile) + + " Test that WinClosed is non-recursive. + new + new + call assert_equal(3, winnr('$')) + let g:triggered = 0 + augroup test-WinClosed + autocmd! + autocmd WinClosed * let g:triggered += 1 + autocmd WinClosed * 2 wincmd c + augroup END + close + call assert_equal(1, winnr('$')) + call assert_equal(1, g:triggered) + + autocmd! test-WinClosed + augroup! test-WinClosed + unlet g:matched + unlet g:amatch + unlet g:afile + unlet g:triggered +endfunc + func s:AddAnAutocmd() augroup vimBarTest au BufReadCmd * echo 'hello' @@ -486,8 +526,7 @@ func Test_autocmd_blast_badd() call writefile(content, 'XblastBall') call system(GetVimCommand() .. ' --clean -S XblastBall') - " call assert_match('OK', readfile('Xerrors')->join()) - call assert_match('OK', join(readfile('Xerrors'))) + call assert_match('OK', readfile('Xerrors')->join()) call delete('XblastBall') call delete('Xerrors') @@ -540,9 +579,10 @@ func Test_empty_doau() endfunc func s:AutoCommandOptionSet(match) + let template = "Option: <%s>, OldVal: <%s>, OldValLocal: <%s>, OldValGlobal: <%s>, NewVal: <%s>, Scope: <%s>, Command: <%s>\n" let item = remove(g:options, 0) - let expected = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", item[0], item[1], item[2], item[3]) - let actual = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", a:match, v:option_old, v:option_new, v:option_type) + let expected = printf(template, item[0], item[1], item[2], item[3], item[4], item[5], item[6]) + let actual = printf(template, a:match, v:option_old, v:option_oldlocal, v:option_oldglobal, v:option_new, v:option_type, v:option_command) let g:opt = [expected, actual] "call assert_equal(expected, actual) endfunc @@ -556,130 +596,593 @@ func Test_OptionSet() au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>")) " 1: Setting number option" - let g:options = [['number', 0, 1, 'global']] + let g:options = [['number', 0, 0, 0, 1, 'global', 'set']] set nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 2: Setting local number option" - let g:options = [['number', 1, 0, 'local']] + let g:options = [['number', 1, 1, '', 0, 'local', 'setlocal']] setlocal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 3: Setting global number option" - let g:options = [['number', 1, 0, 'global']] + let g:options = [['number', 1, '', 1, 0, 'global', 'setglobal']] setglobal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 4: Setting local autoindent option" - let g:options = [['autoindent', 0, 1, 'local']] + let g:options = [['autoindent', 0, 0, '', 1, 'local', 'setlocal']] setlocal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 5: Setting global autoindent option" - let g:options = [['autoindent', 0, 1, 'global']] + let g:options = [['autoindent', 0, '', 0, 1, 'global', 'setglobal']] setglobal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 6: Setting global autoindent option" - let g:options = [['autoindent', 1, 0, 'global']] + let g:options = [['autoindent', 1, 1, 1, 0, 'global', 'set']] + set ai! + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 6a: Setting global autoindent option" + let g:options = [['autoindent', 1, 1, 0, 0, 'global', 'set']] + noa setlocal ai + noa setglobal noai set ai! call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " Should not print anything, use :noa " 7: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nonu - call assert_equal([['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 8: Setting several global list and number option" - let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']] + let g:options = [['list', 0, 0, 0, 1, 'global', 'set'], ['number', 0, 0, 0, 1, 'global', 'set']] set list nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 9: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nolist nonu - call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 10: Setting global acd" - let g:options = [['autochdir', 0, 1, 'local']] + let g:options = [['autochdir', 0, 0, '', 1, 'local', 'setlocal']] setlocal acd call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 11: Setting global autoread (also sets local value)" - let g:options = [['autoread', 0, 1, 'global']] + let g:options = [['autoread', 0, 0, 0, 1, 'global', 'set']] set ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 12: Setting local autoread" - let g:options = [['autoread', 1, 1, 'local']] + let g:options = [['autoread', 1, 1, '', 1, 'local', 'setlocal']] setlocal ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 13: Setting global autoread" - let g:options = [['autoread', 1, 0, 'global']] + let g:options = [['autoread', 1, '', 1, 0, 'global', 'setglobal']] setglobal invar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 14: Setting option backspace through :let" - let g:options = [['backspace', '', 'eol,indent,start', 'global']] + let g:options = [['backspace', '', '', '', 'eol,indent,start', 'global', 'set']] let &bs = "eol,indent,start" call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 15: Setting option backspace through setbufvar()" - let g:options = [['backup', 0, 1, 'local']] + let g:options = [['backup', 0, 0, '', 1, 'local', 'setlocal']] " try twice, first time, shouldn't trigger because option name is invalid, " second time, it should trigger - call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355") + let bnum = bufnr('%') + call assert_fails("call setbufvar(bnum, '&l:bk', 1)", 'E355:') " should trigger, use correct option name - call setbufvar(1, '&backup', 1) + call setbufvar(bnum, '&backup', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 16: Setting number option using setwinvar" - let g:options = [['number', 0, 1, 'local']] + let g:options = [['number', 0, 0, '', 1, 'local', 'setlocal']] call setwinvar(0, '&number', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 17: Setting key option, shouldn't trigger" - let g:options = [['key', 'invalid', 'invalid1', 'invalid']] + let g:options = [['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']] setlocal key=blah setlocal key= - call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options) + call assert_equal([['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 18a: Setting string global option" + let oldval = &backupext + let g:options = [['backupext', oldval, oldval, oldval, 'foo', 'global', 'set']] + set backupext=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18b: Resetting string global option" + let g:options = [['backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set backupext& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18c: Setting global string global option" + let g:options = [['backupext', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal backupext=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18d: Setting local string global option" + " As this is a global option this sets the global value even though + " :setlocal is used! + noa set backupext& " Reset global and local value (without triggering autocmd) + let g:options = [['backupext', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal backupext=baz + call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 18: Setting string option" + " 18e: Setting again string global option" + noa setglobal backupext=ext_global " Reset global and local value (without triggering autocmd) + noa setlocal backupext=ext_local " Sets the global(!) value! + let g:options = [['backupext', 'ext_local', 'ext_local', 'ext_local', 'fuu', 'global', 'set']] + set backupext=fuu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 19a: Setting string global-local (to buffer) option" let oldval = &tags - let g:options = [['tags', oldval, 'tagpath', 'global']] + let g:options = [['tags', oldval, oldval, oldval, 'tagpath', 'global', 'set']] set tags=tagpath call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 1l: Resetting string option" - let g:options = [['tags', 'tagpath', oldval, 'global']] + " 19b: Resetting string global-local (to buffer) option" + let g:options = [['tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set']] set tags& call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) + " 19c: Setting global string global-local (to buffer) option " + let g:options = [['tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal']] + setglobal tags=tagpath1 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19d: Setting local string global-local (to buffer) option" + let g:options = [['tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal']] + setlocal tags=tagpath2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19e: Setting again string global-local (to buffer) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags=tag_local + let g:options = [['tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19f: Setting string global-local (to buffer) option to an empty string" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa set tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags= " empty string + let g:options = [['tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 20a: Setting string local (to buffer) option" + let oldval = &spelllang + let g:options = [['spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set']] + set spelllang=elvish,klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20b: Resetting string local (to buffer) option" + let g:options = [['spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set']] + set spelllang& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20c: Setting global string local (to buffer) option" + let g:options = [['spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal']] + setglobal spelllang=elvish + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20d: Setting local string local (to buffer) option" + noa set spelllang& " Reset global and local value (without triggering autocmd) + let g:options = [['spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal']] + setlocal spelllang=klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20e: Setting again string local (to buffer) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal spelllang=spellglobal " Reset global and local value (without triggering autocmd) + noa setlocal spelllang=spelllocal + let g:options = [['spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set']] + set spelllang=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 21a: Setting string global-local (to window) option" + let oldval = &statusline + let g:options = [['statusline', oldval, oldval, oldval, 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21b: Resetting string global-local (to window) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + let g:options = [['statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set statusline& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21c: Setting global string global-local (to window) option" + let g:options = [['statusline', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal statusline=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21d: Setting local string global-local (to window) option" + noa set statusline& " Reset global and local value (without triggering autocmd) + let g:options = [['statusline', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal statusline=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21e: Setting again string global-local (to window) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal statusline=bar " Reset global and local value (without triggering autocmd) + noa setlocal statusline=baz + let g:options = [['statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 22a: Setting string local (to window) option" + let oldval = &foldignore + let g:options = [['foldignore', oldval, oldval, oldval, 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22b: Resetting string local (to window) option" + let g:options = [['foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set']] + set foldignore& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22c: Setting global string local (to window) option" + let g:options = [['foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal foldignore=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22d: Setting local string local (to window) option" + noa set foldignore& " Reset global and local value (without triggering autocmd) + let g:options = [['foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal foldignore=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22e: Setting again string local (to window) option" + noa setglobal foldignore=glob " Reset global and local value (without triggering autocmd) + noa setlocal foldignore=loc + let g:options = [['foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 23a: Setting global number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '', '1', '2', 'global', 'setglobal']] + setglobal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23b: Setting local number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '', '2', 'local', 'setlocal']] + setlocal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23c: Setting again number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '1', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23d: Setting again number global option" + noa set cmdheight=8 " Reset global and local value (without triggering autocmd) + let g:options = [['cmdheight', '8', '8', '8', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 24a: Setting global number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '8', '', '8', '2', 'global', 'setglobal']] + setglobal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24b: Setting local number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '', '2', 'local', 'setlocal']] + setlocal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24c: Setting again number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24d: Setting again global number global-local (to buffer) option" + noa set undolevels=8 " Reset global and local value (without triggering autocmd) + let g:options = [['undolevels', '8', '8', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 25a: Setting global number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '8', '', '8', '2', 'global', 'setglobal']] + setglobal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25b: Setting local number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '', '2', 'local', 'setlocal']] + setlocal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25c: Setting again number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25d: Setting again global number local (to buffer) option" + noa set wrapmargin=8 " Reset global and local value (without triggering autocmd) + let g:options = [['wrapmargin', '8', '8', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 26: Setting number global-local (to window) option. + " Such option does currently not exist. + + + " 27a: Setting global number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '8', '', '8', '2', 'global', 'setglobal']] + setglobal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27b: Setting local number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '', '2', 'local', 'setlocal']] + setlocal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27c: Setting again number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27d: Setting again global number local (to window) option" + noa set foldcolumn=8 " Reset global and local value (without triggering autocmd) + let g:options = [['foldcolumn', '8', '8', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 28a: Setting global boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '', '1', '0', 'global', 'setglobal']] + setglobal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28b: Setting local boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28c: Setting again boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '1', '0', 'global', 'set']] + set nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28d: Setting again global boolean global option" + noa set nowrapscan " Reset global and local value (without triggering autocmd) + let g:options = [['wrapscan', '0', '0', '0', '1', 'global', 'set']] + set wrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 29a: Setting global boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '0', '', '0', '1', 'global', 'setglobal']] + setglobal autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29b: Setting local boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '', '0', 'local', 'setlocal']] + setlocal noautoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29c: Setting again boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29d: Setting again global boolean global-local (to buffer) option" + noa set noautoread " Reset global and local value (without triggering autocmd) + let g:options = [['autoread', '0', '0', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 30a: Setting global boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30b: Setting local boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30c: Setting again boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30d: Setting again global boolean local (to buffer) option" + noa set nocindent " Reset global and local value (without triggering autocmd) + let g:options = [['cindent', '0', '0', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 31: Setting boolean global-local (to window) option + " Currently no such option exists. + + + " 32a: Setting global boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32b: Setting local boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32c: Setting again boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32d: Setting again global boolean local (to window) option" + noa set nocursorcolumn " Reset global and local value (without triggering autocmd) + let g:options = [['cursorcolumn', '0', '0', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 33: Test autocommands when an option value is converted internally. + noa set backspace=1 " Reset global and local value (without triggering autocmd) + let g:options = [['backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set']] + set backspace=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " Cleanup au! OptionSet " set tags& - for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'tags'] + for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'backupext', 'tags', 'spelllang', 'statusline', 'foldignore', 'cmdheight', 'undolevels', 'wrapmargin', 'foldcolumn', 'wrapscan', 'autoread', 'cindent', 'cursorcolumn'] exe printf(":set %s&vim", opt) endfor call test_override('starting', 0) @@ -1857,6 +2360,26 @@ func Test_autocmd_CmdWinEnter() call delete(filename) endfunc +func Test_autocmd_was_using_freed_memory() + pedit xx + n x + augroup winenter + au WinEnter * if winnr('$') > 2 | quit | endif + augroup END + " Nvim needs large 'winwidth' and 'nowinfixwidth' to crash + set winwidth=99999 nowinfixwidth + split + + augroup winenter + au! WinEnter + augroup END + + set winwidth& winfixwidth& + bwipe xx + bwipe x + pclose +endfunc + func Test_FileChangedShell_reload() if !has('unix') return @@ -2085,6 +2608,19 @@ func Test_autocmd_closes_window() au! BufWinLeave endfunc +func Test_autocmd_quit_psearch() + sn aa bb + augroup aucmd_win_test + au! + au BufEnter,BufLeave,BufNew,WinEnter,WinLeave,WinNew * if winnr('$') > 1 | q | endif + augroup END + ps / + + augroup aucmd_win_test + au! + augroup END +endfunc + func Test_autocmd_closing_cmdwin() au BufWinLeave * nested q call assert_fails("norm 7q?\n", 'E855:') diff --git a/src/nvim/testdir/test_blockedit.vim b/src/nvim/testdir/test_blockedit.vim index 180524cd73..38978ef689 100644 --- a/src/nvim/testdir/test_blockedit.vim +++ b/src/nvim/testdir/test_blockedit.vim @@ -15,6 +15,58 @@ func Test_blockinsert_indent() bwipe! endfunc +func Test_blockinsert_autoindent() + new + let lines =<< trim END + var d = { + a: () => 0, + b: () => 0, + c: () => 0, + } + END + call setline(1, lines) + filetype plugin indent on + setlocal sw=2 et ft=vim + setlocal indentkeys+=: + exe "norm! 2Gf)\<c-v>2jA: asdf\<esc>" + let expected =<< trim END + var d = { + a: (): asdf => 0, + b: (): asdf => 0, + c: (): asdf => 0, + } + END + call assert_equal(expected, getline(1, 5)) + + " insert on the next column should do exactly the same + :%dele + call setline(1, lines) + exe "norm! 2Gf)l\<c-v>2jI: asdf\<esc>" + call assert_equal(expected, getline(1, 5)) + + :%dele + call setline(1, lines) + setlocal sw=8 noet + exe "norm! 2Gf)\<c-v>2jA: asdf\<esc>" + let expected =<< trim END + var d = { + a: (): asdf => 0, + b: (): asdf => 0, + c: (): asdf => 0, + } + END + call assert_equal(expected, getline(1, 5)) + + " insert on the next column should do exactly the same + :%dele + call setline(1, lines) + exe "norm! 2Gf)l\<c-v>2jI: asdf\<esc>" + call assert_equal(expected, getline(1, 5)) + + filetype off + bwipe! +endfunc + func Test_blockinsert_delete() new let _bs = &bs diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index 277050876e..8d592f21ea 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -432,7 +432,7 @@ func Test_breakindent11_vartabs() call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4 vts=4') let text = getline(2) let width = strlen(text[1:]) + 2->indent() + strlen(&sbr) * 3 " text wraps 3 times - call assert_equal(width, strdisplaywidth(text)) + call assert_equal(width, text->strdisplaywidth()) call s:close_windows('set sbr= vts&') endfunc diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim index bb672cf0ec..a6eb93b4be 100644 --- a/src/nvim/testdir/test_bufwintabinfo.vim +++ b/src/nvim/testdir/test_bufwintabinfo.vim @@ -1,108 +1,113 @@ " Tests for the getbufinfo(), getwininfo() and gettabinfo() functions function Test_getbufwintabinfo() - edit Xtestfile1 - edit Xtestfile2 - let buflist = getbufinfo() - call assert_equal(2, len(buflist)) - call assert_match('Xtestfile1', buflist[0].name) - call assert_match('Xtestfile2', getbufinfo('Xtestfile2')[0].name) - call assert_equal([], getbufinfo(2016)) - edit Xtestfile1 - hide edit Xtestfile2 - hide enew - call assert_equal(3, len(getbufinfo({'bufloaded':1}))) - - set tabstop&vim - let b:editor = 'vim' + edit Xtestfile1 + edit Xtestfile2 + let buflist = getbufinfo() + call assert_equal(2, len(buflist)) + call assert_match('Xtestfile1', buflist[0].name) + call assert_match('Xtestfile2', getbufinfo('Xtestfile2')[0].name) + call assert_equal([], getbufinfo(2016)) + edit Xtestfile1 + hide edit Xtestfile2 + hide enew + call assert_equal(3, len(getbufinfo({'bufloaded':1}))) + + set tabstop&vim + let b:editor = 'vim' + let l = getbufinfo('%') + call assert_equal(bufnr('%'), l[0].bufnr) + call assert_equal('vim', l[0].variables.editor) + call assert_notequal(-1, index(l[0].windows, '%'->bufwinid())) + + let l = '%'->getbufinfo() + call assert_equal(bufnr('%'), l[0].bufnr) + + " Test for getbufinfo() with 'bufmodified' + call assert_equal(0, len(getbufinfo({'bufmodified' : 1}))) + call setbufline('Xtestfile1', 1, ["Line1"]) + let l = getbufinfo({'bufmodified' : 1}) + call assert_equal(1, len(l)) + call assert_equal(bufnr('Xtestfile1'), l[0].bufnr) + + if has('signs') + call append(0, ['Linux', 'Windows', 'Mac']) + sign define Mark text=>> texthl=Search + exe "sign place 2 line=3 name=Mark buffer=" . bufnr('%') let l = getbufinfo('%') - call assert_equal(bufnr('%'), l[0].bufnr) - call assert_equal('vim', l[0].variables.editor) - call assert_notequal(-1, index(l[0].windows, '%'->bufwinid())) - - " Test for getbufinfo() with 'bufmodified' - call assert_equal(0, len(getbufinfo({'bufmodified' : 1}))) - call setbufline('Xtestfile1', 1, ["Line1"]) - let l = getbufinfo({'bufmodified' : 1}) - call assert_equal(1, len(l)) - call assert_equal(bufnr('Xtestfile1'), l[0].bufnr) - - if has('signs') - call append(0, ['Linux', 'Windows', 'Mac']) - sign define Mark text=>> texthl=Search - exe "sign place 2 line=3 name=Mark buffer=" . bufnr('%') - let l = getbufinfo('%') - call assert_equal(2, l[0].signs[0].id) - call assert_equal(3, l[0].signs[0].lnum) - call assert_equal('Mark', l[0].signs[0].name) - sign unplace * - sign undefine Mark - enew! - endif - - only - let w1_id = win_getid() - new - let w2_id = win_getid() - tabnew | let w3_id = win_getid() - new | let w4_id = win_getid() - vert new | let w5_id = win_getid() - call setwinvar(0, 'signal', 'green') - tabfirst - let winlist = getwininfo() - call assert_equal(5, len(winlist)) - call assert_equal(winwidth(1), winlist[0].width) - call assert_equal(1, winlist[0].wincol) - " tabline adds one row in terminal, not in GUI - let tablineheight = winlist[0].winrow == 2 ? 1 : 0 - call assert_equal(tablineheight + 1, winlist[0].winrow) - - call assert_equal(winbufnr(2), winlist[1].bufnr) - call assert_equal(winheight(2), winlist[1].height) - call assert_equal(1, winlist[1].wincol) - call assert_equal(tablineheight + winheight(1) + 2, winlist[1].winrow) - - call assert_equal(1, winlist[2].winnr) - call assert_equal(tablineheight + 1, winlist[2].winrow) - call assert_equal(1, winlist[2].wincol) - - call assert_equal(winlist[2].width + 2, winlist[3].wincol) - call assert_equal(1, winlist[4].wincol) - - call assert_equal(1, winlist[0].tabnr) - call assert_equal(1, winlist[1].tabnr) - call assert_equal(2, winlist[2].tabnr) - call assert_equal(2, winlist[3].tabnr) - call assert_equal(2, winlist[4].tabnr) - - call assert_equal('green', winlist[2].variables.signal) - call assert_equal(w4_id, winlist[3].winid) - let winfo = w5_id->getwininfo()[0] - call assert_equal(2, winfo.tabnr) - call assert_equal([], getwininfo(3)) - - call settabvar(1, 'space', 'build') - let tablist = gettabinfo() - call assert_equal(2, len(tablist)) - call assert_equal(3, len(tablist[1].windows)) - call assert_equal(2, tablist[1].tabnr) - call assert_equal('build', tablist[0].variables.space) - call assert_equal(w2_id, tablist[0].windows[0]) - call assert_equal([], 3->gettabinfo()) - - tabonly | only - - lexpr '' - lopen - copen - let winlist = getwininfo() - call assert_false(winlist[0].quickfix) - call assert_false(winlist[0].loclist) - call assert_true(winlist[1].quickfix) - call assert_true(winlist[1].loclist) - call assert_true(winlist[2].quickfix) - call assert_false(winlist[2].loclist) - wincmd t | only + call assert_equal(2, l[0].signs[0].id) + call assert_equal(3, l[0].signs[0].lnum) + call assert_equal('Mark', l[0].signs[0].name) + sign unplace * + sign undefine Mark + enew! + endif + + only + let w1_id = win_getid() + setl foldcolumn=3 + new + let w2_id = win_getid() + tabnew | let w3_id = win_getid() + new | let w4_id = win_getid() + vert new | let w5_id = win_getid() + eval 'green'->setwinvar(0, 'signal') + tabfirst + let winlist = getwininfo() + call assert_equal(5, len(winlist)) + call assert_equal(winwidth(1), winlist[0].width) + call assert_equal(1, winlist[0].wincol) + " tabline adds one row in terminal, not in GUI + let tablineheight = winlist[0].winrow == 2 ? 1 : 0 + call assert_equal(tablineheight + 1, winlist[0].winrow) + + call assert_equal(winbufnr(2), winlist[1].bufnr) + call assert_equal(winheight(2), winlist[1].height) + call assert_equal(1, winlist[1].wincol) + call assert_equal(3, winlist[1].textoff) " foldcolumn + call assert_equal(tablineheight + winheight(1) + 2, winlist[1].winrow) + + call assert_equal(1, winlist[2].winnr) + call assert_equal(tablineheight + 1, winlist[2].winrow) + call assert_equal(1, winlist[2].wincol) + + call assert_equal(winlist[2].width + 2, winlist[3].wincol) + call assert_equal(1, winlist[4].wincol) + + call assert_equal(1, winlist[0].tabnr) + call assert_equal(1, winlist[1].tabnr) + call assert_equal(2, winlist[2].tabnr) + call assert_equal(2, winlist[3].tabnr) + call assert_equal(2, winlist[4].tabnr) + + call assert_equal('green', winlist[2].variables.signal) + call assert_equal(w4_id, winlist[3].winid) + let winfo = w5_id->getwininfo()[0] + call assert_equal(2, winfo.tabnr) + call assert_equal([], getwininfo(3)) + + call settabvar(1, 'space', 'build') + let tablist = gettabinfo() + call assert_equal(2, len(tablist)) + call assert_equal(3, len(tablist[1].windows)) + call assert_equal(2, tablist[1].tabnr) + call assert_equal('build', tablist[0].variables.space) + call assert_equal(w2_id, tablist[0].windows[0]) + call assert_equal([], 3->gettabinfo()) + + tabonly | only + + lexpr '' + lopen + copen + let winlist = getwininfo() + call assert_false(winlist[0].quickfix) + call assert_false(winlist[0].loclist) + call assert_true(winlist[1].quickfix) + call assert_true(winlist[1].loclist) + call assert_true(winlist[2].quickfix) + call assert_false(winlist[2].loclist) + wincmd t | only endfunction function Test_get_buf_options() diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index 0bba321ee2..76a2620be0 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -101,7 +101,7 @@ func Test_chdir_func() call assert_match('^\[global\] .*/Xdir$', trim(execute('verbose pwd'))) call chdir('..') call assert_equal('y', fnamemodify(getcwd(1, 2), ':t')) - call assert_equal('z', fnamemodify(getcwd(3, 2), ':t')) + call assert_equal('z', fnamemodify(3->getcwd(2), ':t')) tabnext | wincmd t call assert_match('^\[tabpage\] .*/y$', trim(execute('verbose pwd'))) call chdir('..') @@ -215,3 +215,42 @@ func Test_cd_from_non_existing_dir() cd - call assert_equal(saveddir, getcwd()) endfunc + +func Test_cd_unknown_dir() + call mkdir('Xa') + cd Xa + call writefile(['text'], 'Xb.txt') + edit Xa/Xb.txt + let first_buf = bufnr() + cd .. + edit + call assert_equal(first_buf, bufnr()) + edit Xa/Xb.txt + call assert_notequal(first_buf, bufnr()) + + bwipe! + exe "bwipe! " .. first_buf + call delete('Xa', 'rf') +endfunc + +func Test_getcwd_actual_dir() + CheckFunction test_autochdir + let startdir = getcwd() + call mkdir('Xactual') + call test_autochdir() + set autochdir + edit Xactual/file.txt + call assert_match('testdir.Xactual$', getcwd()) + lcd .. + call assert_match('testdir$', getcwd()) + edit + call assert_match('testdir.Xactual$', getcwd()) + call assert_match('testdir$', getcwd(win_getid())) + + set noautochdir + bwipe! + call chdir(startdir) + call delete('Xactual', 'rf') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim index 17a49e02be..6f09e85a42 100644 --- a/src/nvim/testdir/test_charsearch.vim +++ b/src/nvim/testdir/test_charsearch.vim @@ -20,7 +20,7 @@ func Test_charsearch() " check that setcharsearch() changes the settings. 3 normal! ylfep - call setcharsearch({'char': 'k'}) + eval {'char': 'k'}->setcharsearch() normal! ;p call setcharsearch({'forward': 0}) normal! $;p diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index f3db472b03..922803438f 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -82,7 +82,7 @@ func Test_client_server() call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid') call assert_equal('got it', g:myserverid->remote_read(2)) - call remote_send(name, ":call server2client(expand('<client>'), 'another')\<CR>", 'g:myserverid') + call remote_send(name, ":eval expand('<client>')->server2client('another')\<CR>", 'g:myserverid') let peek_result = 'nothing' let r = g:myserverid->remote_peek('peek_result') " unpredictable whether the result is already available. diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 98340d0ac6..49a5386337 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -428,14 +428,17 @@ func Test_getcompletion() call assert_equal([], l) func T(a, c, p) + let g:cmdline_compl_params = [a:a, a:c, a:p] return "oneA\noneB\noneC" endfunc command -nargs=1 -complete=custom,T MyCmd let l = getcompletion('MyCmd ', 'cmdline') call assert_equal(['oneA', 'oneB', 'oneC'], l) + call assert_equal(['', 'MyCmd ', 6], g:cmdline_compl_params) delcommand MyCmd delfunc T + unlet g:cmdline_compl_params " For others test if the name is recognized. let names = ['buffer', 'environment', 'file_in_path', 'mapping', 'tag', 'tag_listfiles', 'user'] @@ -902,7 +905,7 @@ func Test_setcmdpos() call assert_equal('"12ab', @:) " setcmdpos() returns 1 when not editing the command line. - call assert_equal(1, setcmdpos(3)) + call assert_equal(1, 3->setcmdpos()) endfunc func Test_cmdline_overstrike() diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index c3de7d0050..aaa2301bca 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -38,10 +38,9 @@ func Test_compiler() endfunc func GetCompilerNames() - " return glob('$VIMRUNTIME/compiler/*.vim', 0, 1) - " \ ->map({i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')}) - " \ ->sort() - return sort(map(glob('$VIMRUNTIME/compiler/*.vim', 0, 1), {i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')})) + return glob('$VIMRUNTIME/compiler/*.vim', 0, 1) + \ ->map({i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')}) + \ ->sort() endfunc func Test_compiler_without_arg() @@ -54,8 +53,7 @@ func Test_compiler_without_arg() endfunc func Test_compiler_completion() - " let clist = GetCompilerNames()->join(' ') - let clist = join(GetCompilerNames(), ' ') + let clist = GetCompilerNames()->join(' ') call feedkeys(":compiler \<C-A>\<C-B>\"\<CR>", 'tx') call assert_match('^"compiler ' .. clist .. '$', @:) diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 46847e0663..e8c4a952ee 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -37,7 +37,7 @@ endfunc " Very short version of what matchparen does. function s:Highlight_Matching_Pair() let save_cursor = getcurpos() - call setpos('.', save_cursor) + eval save_cursor->setpos('.') endfunc func Test_curswant_with_autocommand() @@ -82,11 +82,11 @@ func Test_screenpos() call assert_equal({'row': winrow, \ 'col': wincol + 0, \ 'curscol': wincol + 7, - \ 'endcol': wincol + 7}, screenpos(winid, 1, 1)) + \ 'endcol': wincol + 7}, winid->screenpos(1, 1)) call assert_equal({'row': winrow, \ 'col': wincol + 13, \ 'curscol': wincol + 13, - \ 'endcol': wincol + 13}, screenpos(winid, 1, 7)) + \ 'endcol': wincol + 13}, winid->screenpos(1, 7)) call assert_equal({'row': winrow + 2, \ 'col': wincol + 1, \ 'curscol': wincol + 1, diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 61da3cbcaa..3a0c615cf6 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -387,7 +387,7 @@ func Test_diffoff() call setline(1, ['One', '', 'Two', 'Three']) diffthis redraw - call assert_notequal(normattr, screenattr(1, 1)) + call assert_notequal(normattr, 1->screenattr(1)) diffoff! redraw call assert_equal(normattr, screenattr(1, 1)) diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 23ad8dbfc5..fc4e80f0d6 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1294,6 +1294,7 @@ func Test_edit_forbidden() call assert_fails(':Sandbox', 'E48:') delcom Sandbox call assert_equal(['a'], getline(1,'$')) + " 2) edit with textlock set fu! DoIt() call feedkeys("i\<del>\<esc>", 'tnix') @@ -1313,6 +1314,7 @@ func Test_edit_forbidden() catch /^Vim\%((\a\+)\)\=:E117/ " catch E117: unknown function endtry au! InsertCharPre + " 3) edit when completion is shown fun! Complete(findstart, base) if a:findstart @@ -1330,6 +1332,7 @@ func Test_edit_forbidden() endtry delfu Complete set completefunc= + if has("rightleft") && exists("+fkmap") " 4) 'R' when 'fkmap' and 'revins' is set. set revins fkmap @@ -1644,4 +1647,95 @@ func Test_read_invalid() set encoding=utf-8 endfunc +" Test for ModeChanged pattern +func Test_mode_changes() + let g:index = 0 + let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] + func! TestMode() + call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode")) + call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode")) + call assert_equal(mode(1), get(v:event, "new_mode")) + let g:index += 1 + endfunc + + au ModeChanged * :call TestMode() + let g:n_to_any = 0 + au ModeChanged n:* let g:n_to_any += 1 + call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix') + + let g:V_to_v = 0 + au ModeChanged V:v let g:V_to_v += 1 + call feedkeys("Vv\<C-G>\<esc>", 'tnix') + call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any) + call assert_equal(1, g:V_to_v) + call assert_equal(len(g:mode_seq) - 1, g:index) + + let g:n_to_i = 0 + au ModeChanged n:i let g:n_to_i += 1 + let g:n_to_niI = 0 + au ModeChanged i:niI let g:n_to_niI += 1 + let g:niI_to_i = 0 + au ModeChanged niI:i let g:niI_to_i += 1 + let g:nany_to_i = 0 + au ModeChanged n*:i let g:nany_to_i += 1 + let g:i_to_n = 0 + au ModeChanged i:n let g:i_to_n += 1 + let g:nori_to_any = 0 + au ModeChanged [ni]:* let g:nori_to_any += 1 + let g:i_to_any = 0 + au ModeChanged i:* let g:i_to_any += 1 + let g:index = 0 + let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] + call feedkeys("a\<C-O>l\<esc>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(3, g:nori_to_any) + + if has('terminal') + let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] + call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(5, g:nori_to_any) + endif + + au! ModeChanged + delfunc TestMode + unlet! g:mode_seq + unlet! g:index + unlet! g:n_to_any + unlet! g:V_to_v + unlet! g:n_to_i + unlet! g:n_to_niI + unlet! g:niI_to_i + unlet! g:nany_to_i + unlet! g:i_to_n + unlet! g:nori_to_any + unlet! g:i_to_any +endfunc + +func Test_recursive_ModeChanged() + au! ModeChanged * norm 0u + sil! norm + au! +endfunc + +func Test_ModeChanged_starts_visual() + " This was triggering ModeChanged before setting VIsual, causing a crash. + au! ModeChanged * norm 0u + sil! norm + + au! ModeChanged +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_environ.vim b/src/nvim/testdir/test_environ.vim index cc15b63824..dd34983ee5 100644 --- a/src/nvim/testdir/test_environ.vim +++ b/src/nvim/testdir/test_environ.vim @@ -22,7 +22,7 @@ endfunc func Test_setenv() unlet! $TESTENV - call setenv('TEST ENV', 'foo') + eval 'foo'->setenv('TEST ENV') call assert_equal('foo', getenv('TEST ENV')) call setenv('TEST ENV', v:null) call assert_equal(v:null, getenv('TEST ENV')) diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index 1c645ad0f8..92e0559618 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -85,7 +85,7 @@ endfunc func Test_ex_mode_count_overflow() " this used to cause a crash let lines =<< trim END - call feedkeys("\<Esc>Q\<CR>") + call feedkeys("\<Esc>gQ\<CR>") v9|9silent! vi|333333233333y32333333%O call writefile(['done'], 'Xdidexmode') qall! diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim index 15ba894dbe..2cb6d73407 100644 --- a/src/nvim/testdir/test_execute_func.vim +++ b/src/nvim/testdir/test_execute_func.vim @@ -99,7 +99,7 @@ func Test_win_execute() if has('textprop') let popupwin = popup_create('the popup win', {'line': 2, 'col': 3}) redraw - let line = win_execute(popupwin, 'echo getline(1)') + let line = 'echo getline(1)'->win_execute(popupwin) call assert_match('the popup win', line) call popup_close(popupwin) @@ -107,6 +107,18 @@ func Test_win_execute() call win_gotoid(otherwin) bwipe! + + " check :lcd in another window does not change directory + let curid = win_getid() + let curdir = getcwd() + split Xother + lcd .. + " Use :pwd to get the actual current directory + let otherdir = execute('pwd') + call win_execute(curid, 'lcd testdir') + call assert_equal(otherdir, execute('pwd')) + bwipe! + execute 'cd ' .. curdir endfunc func Test_win_execute_update_ruler() diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 6343c47fde..1d7fd3e385 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -56,7 +56,7 @@ endfunc func Test_strgetchar() call assert_equal(char2nr('a'), strgetchar('axb', 0)) - call assert_equal(char2nr('x'), strgetchar('axb', 1)) + call assert_equal(char2nr('x'), 'axb'->strgetchar(1)) call assert_equal(char2nr('b'), strgetchar('axb', 2)) call assert_equal(-1, strgetchar('axb', -1)) @@ -66,7 +66,7 @@ endfunc func Test_strcharpart() call assert_equal('a', strcharpart('axb', 0, 1)) - call assert_equal('x', strcharpart('axb', 1, 1)) + call assert_equal('x', 'axb'->strcharpart(1, 1)) call assert_equal('b', strcharpart('axb', 2, 1)) call assert_equal('xb', strcharpart('axb', 1)) @@ -493,7 +493,7 @@ func Test_setmatches() let set[0]['conceal'] = 5 let exp[0]['conceal'] = '5' endif - call setmatches(set) + eval set->setmatches() call assert_equal(exp, getmatches()) endfunc diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 18e59bb6b7..dbe0cd8388 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -59,7 +59,7 @@ let s:filename_checks = { \ 'aml': ['file.aml'], \ 'ampl': ['file.run'], \ 'ant': ['build.xml'], - \ 'apache': ['.htaccess', '/etc/httpd/file.conf', '/etc/apache2/sites-2/file.com', '/etc/apache2/some.config', '/etc/apache2/conf.file/conf', '/etc/apache2/mods-some/file', '/etc/apache2/sites-some/file', '/etc/httpd/conf.d/file.config', '/etc/apache2/conf.file/file', '/etc/apache2/file.conf', '/etc/apache2/file.conf-file', '/etc/apache2/mods-file/file', '/etc/apache2/sites-file/file', '/etc/apache2/sites-file/file.com', '/etc/httpd/conf.d/file.conf', '/etc/httpd/conf.d/file.conf-file', 'access.conf', 'access.conf-file', 'any/etc/apache2/conf.file/file', 'any/etc/apache2/file.conf', 'any/etc/apache2/file.conf-file', 'any/etc/apache2/mods-file/file', 'any/etc/apache2/sites-file/file', 'any/etc/apache2/sites-file/file.com', 'any/etc/httpd/conf.d/file.conf', 'any/etc/httpd/conf.d/file.conf-file', 'any/etc/httpd/file.conf', 'apache.conf', 'apache.conf-file', 'apache2.conf', 'apache2.conf-file', 'httpd.conf', 'httpd.conf-file', 'srm.conf', 'srm.conf-file'], + \ 'apache': ['.htaccess', '/etc/httpd/file.conf', '/etc/apache2/sites-2/file.com', '/etc/apache2/some.config', '/etc/apache2/conf.file/conf', '/etc/apache2/mods-some/file', '/etc/apache2/sites-some/file', '/etc/httpd/conf.d/file.config', '/etc/apache2/conf.file/file', '/etc/apache2/file.conf', '/etc/apache2/file.conf-file', '/etc/apache2/mods-file/file', '/etc/apache2/sites-file/file', '/etc/apache2/sites-file/file.com', '/etc/httpd/conf.d/file.conf', '/etc/httpd/conf.d/file.conf-file', 'access.conf', 'access.conf-file', 'any/etc/apache2/conf.file/file', 'any/etc/apache2/file.conf', 'any/etc/apache2/file.conf-file', 'any/etc/apache2/mods-file/file', 'any/etc/apache2/sites-file/file', 'any/etc/apache2/sites-file/file.com', 'any/etc/httpd/conf.d/file.conf', 'any/etc/httpd/conf.d/file.conf-file', 'any/etc/httpd/file.conf', 'apache.conf', 'apache.conf-file', 'apache2.conf', 'apache2.conf-file', 'httpd.conf', 'httpd.conf-file', 'srm.conf', 'srm.conf-file', '/etc/httpd/mods-some/file', '/etc/httpd/sites-some/file', '/etc/httpd/conf.file/conf'], \ 'apachestyle': ['/etc/proftpd/file.config,/etc/proftpd/conf.file/file', '/etc/proftpd/conf.file/file', '/etc/proftpd/file.conf', '/etc/proftpd/file.conf-file', 'any/etc/proftpd/conf.file/file', 'any/etc/proftpd/file.conf', 'any/etc/proftpd/file.conf-file', 'proftpd.conf', 'proftpd.conf-file'], \ 'applescript': ['file.scpt'], \ 'aptconf': ['apt.conf', '/.aptitude/config', 'any/.aptitude/config'], @@ -117,7 +117,7 @@ let s:filename_checks = { \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'], \ 'crm': ['file.crm'], \ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'], - \ 'cs': ['file.cs'], + \ 'cs': ['file.cs', 'file.csx'], \ 'csc': ['file.csc'], \ 'csdl': ['file.csdl'], \ 'csp': ['file.csp', 'file.fdr'], @@ -142,7 +142,7 @@ let s:filename_checks = { \ 'desc': ['file.desc'], \ 'desktop': ['file.desktop', '.directory', 'file.directory'], \ 'dictconf': ['dict.conf', '.dictrc'], - \ 'dictdconf': ['dictd.conf'], + \ 'dictdconf': ['dictd.conf', 'dictdfile.conf', 'dictd-file.conf'], \ 'diff': ['file.diff', 'file.rej'], \ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS', 'any/etc/DIR_COLORS'], \ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'], @@ -180,15 +180,17 @@ let s:filename_checks = { \ 'fennel': ['file.fnl'], \ 'fetchmail': ['.fetchmailrc'], \ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'], + \ 'fish': ['file.fish'], \ 'focexec': ['file.fex', 'file.focexec'], - \ 'forth': ['file.fs', 'file.ft', 'file.fth'], + \ 'forth': ['file.ft', 'file.fth'], \ 'fortran': ['file.f', 'file.for', 'file.fortran', 'file.fpp', 'file.ftn', 'file.f77', 'file.f90', 'file.f95', 'file.f03', 'file.f08'], \ 'fpcmake': ['file.fpc'], \ 'framescript': ['file.fsl'], \ 'freebasic': ['file.fb', 'file.bi'], + \ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'], \ 'fstab': ['fstab', 'mtab'], \ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'], - \ 'gdb': ['.gdbinit'], + \ 'gdb': ['.gdbinit', 'gdbinit'], \ 'gdmo': ['file.mo', 'file.gdmo'], \ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'], \ 'gemtext': ['file.gmi', 'file.gemini'], @@ -224,6 +226,7 @@ let s:filename_checks = { \ 'hollywood': ['file.hws'], \ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'], \ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny', 'any/etc/hosts.allow', 'any/etc/hosts.deny'], + \ 'i3config': ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'], \ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'], \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig'], \ 'natural': ['file.NSA', 'file.NSC', 'file.NSG', 'file.NSL', 'file.NSM', 'file.NSN', 'file.NSP', 'file.NSS'], @@ -337,7 +340,7 @@ let s:filename_checks = { \ 'msql': ['file.msql'], \ 'mupad': ['file.mu'], \ 'mush': ['file.mush'], - \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'], + \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', '/etc/Muttrc.d/file.rc', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'], \ 'mysql': ['file.mysql'], \ 'n1ql': ['file.n1ql', 'file.nql'], \ 'named': ['namedfile.conf', 'rndcfile.conf', 'named-file.conf', 'named.conf', 'rndc-file.conf', 'rndc-file.key', 'rndc.conf', 'rndc.key'], @@ -419,6 +422,7 @@ let s:filename_checks = { \ 'rnc': ['file.rnc'], \ 'rng': ['file.rng'], \ 'robots': ['robots.txt'], + \ 'routeros': ['file.rsc'], \ 'rpcgen': ['file.x'], \ 'rpl': ['file.rpl'], \ 'rst': ['file.rst'], @@ -452,6 +456,7 @@ let s:filename_checks = { \ 'skill': ['file.il', 'file.ils', 'file.cdf'], \ 'slang': ['file.sl'], \ 'slice': ['file.ice'], + \ 'solution': ['file.sln'], \ 'slpconf': ['/etc/slp.conf', 'any/etc/slp.conf'], \ 'slpreg': ['/etc/slp.reg', 'any/etc/slp.reg'], \ 'slpspi': ['/etc/slp.spi', 'any/etc/slp.spi'], @@ -473,13 +478,14 @@ let s:filename_checks = { \ 'sqlj': ['file.sqlj'], \ 'sqr': ['file.sqr', 'file.sqi'], \ 'squid': ['squid.conf'], + \ 'squirrel': ['file.nut'], \ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'], \ 'sshconfig': ['ssh_config', '/.ssh/config', '/etc/ssh/ssh_config.d/file.conf', 'any/etc/ssh/ssh_config.d/file.conf', 'any/.ssh/config'], \ 'sshdconfig': ['sshd_config', '/etc/ssh/sshd_config.d/file.conf', 'any/etc/ssh/sshd_config.d/file.conf'], \ 'st': ['file.st'], \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], - \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers'], + \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'], \ 'svg': ['file.svg'], \ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'], \ 'swift': ['file.swift'], @@ -498,7 +504,7 @@ let s:filename_checks = { \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], - \ 'text': ['file.text', 'README', '/usr/share/doc/bash-completion/AUTHORS'], + \ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], \ 'tf': ['file.tf', '.tfrc', 'tfrc'], \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'], \ 'tilde': ['file.t.html'], @@ -551,7 +557,7 @@ let s:filename_checks = { \ 'xhtml': ['file.xhtml', 'file.xht'], \ 'xinetd': ['/etc/xinetd.conf', '/etc/xinetd.d/file', 'any/etc/xinetd.conf', 'any/etc/xinetd.d/file'], \ 'xmath': ['file.msc', 'file.msf'], - \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu'], + \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.fsproj', 'file.fsproj.user', 'file.vbproj', 'file.vbproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1', 'file.mpd'], \ 'xmodmap': ['anyXmodmap', 'Xmodmap', 'some-Xmodmap', 'some-xmodmap', 'some-xmodmap-file', 'xmodmap', 'xmodmap-file'], \ 'xf86conf': ['xorg.conf', 'xorg.conf-4'], \ 'xpm': ['file.xpm'], @@ -564,6 +570,7 @@ let s:filename_checks = { \ 'yaml': ['file.yaml', 'file.yml'], \ 'raml': ['file.raml'], \ 'z8a': ['file.z8a'], + \ 'zig': ['file.zig'], \ 'zimbu': ['file.zu'], \ 'zimbutempl': ['file.zut'], \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'], @@ -659,6 +666,9 @@ let s:script_checks = { \ 'yaml': [['%YAML 1.2']], \ 'pascal': [['#!/path/instantfpc']], \ 'fennel': [['#!/path/fennel']], + \ 'routeros': [['#!/path/rsc']], + \ 'fish': [['#!/path/fish']], + \ 'forth': [['#!/path/gforth']], \ } " Various forms of "env" optional arguments. @@ -862,6 +872,16 @@ func Test_m_file() call assert_equal('objc', &filetype) bwipe! + call writefile(['#include <header.h>'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) + bwipe! + + call writefile(['#define FORTY_TWO'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) + bwipe! + " Octave call writefile(['# Octave line comment'], 'Xfile.m') @@ -931,4 +951,97 @@ func Test_xpm_file() filetype off endfunc +func Test_fs_file() + filetype on + + call writefile(['looks like F#'], 'Xfile.fs') + split Xfile.fs + call assert_equal('fsharp', &filetype) + bwipe! + + let g:filetype_fs = 'forth' + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + unlet g:filetype_fs + + " Test dist#ft#FTfs() + + " Forth (Gforth) + + call writefile(['( Forth inline comment )'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile(['.( Forth displayed inline comment )'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile(['\ Forth line comment'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + " empty line comment - no space required + call writefile(['\'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile(['\G Forth documentation comment '], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile([': squared ( n -- n^2 )', 'dup * ;'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call delete('Xfile.fs') + filetype off +endfunc + +func Test_dep3patch_file() + filetype on + + call assert_true(mkdir('debian/patches', 'p')) + + " series files are not patches + call writefile(['Description: some awesome patch'], 'debian/patches/series') + split debian/patches/series + call assert_notequal('dep3patch', &filetype) + bwipe! + + " diff/patch files without the right headers should still show up as ft=diff + call writefile([], 'debian/patches/foo.diff') + split debian/patches/foo.diff + call assert_equal('diff', &filetype) + bwipe! + + " Files with the right headers are detected as dep3patch, even if they don't + " have a diff/patch extension + call writefile(['Subject: dep3patches'], 'debian/patches/bar') + split debian/patches/bar + call assert_equal('dep3patch', &filetype) + bwipe! + + " Files in sub-directories are detected + call assert_true(mkdir('debian/patches/s390x', 'p')) + call writefile(['Subject: dep3patches'], 'debian/patches/s390x/bar') + split debian/patches/s390x/bar + call assert_equal('dep3patch', &filetype) + bwipe! + + " The detection stops when seeing the "header end" marker + call writefile(['---', 'Origin: the cloud'], 'debian/patches/baz') + split debian/patches/baz + call assert_notequal('dep3patch', &filetype) + bwipe! + + call delete('debian/patches', 'rf') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 5586fe2151..6da1b3d4a0 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -809,8 +809,7 @@ func Test_undo_fold_deletion() g/"/d undo redo - " eval getline(1, '$')->assert_equal(['']) - eval assert_equal(getline(1, '$'), ['']) + eval getline(1, '$')->assert_equal(['']) set fdm&vim bwipe! diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index d10fad690c..0edbeb420a 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -143,7 +143,7 @@ func Test_str2nr() call assert_equal(-123456789, str2nr('-123456789')) call assert_equal(5, str2nr('101', 2)) - call assert_equal(5, str2nr('0b101', 2)) + call assert_equal(5, '0b101'->str2nr(2)) call assert_equal(5, str2nr('0B101', 2)) call assert_equal(-5, str2nr('-101', 2)) call assert_equal(-5, str2nr('-0b101', 2)) @@ -200,7 +200,7 @@ func Test_strftime() " of strftime() can be 17 or 18, depending on timezone. call assert_match('^2017-01-1[78]$', strftime('%Y-%m-%d', 1484695512)) " - call assert_match('^\d\d\d\d-\(0\d\|1[012]\)-\([012]\d\|3[01]\) \([01]\d\|2[0-3]\):[0-5]\d:\([0-5]\d\|60\)$', strftime('%Y-%m-%d %H:%M:%S')) + call assert_match('^\d\d\d\d-\(0\d\|1[012]\)-\([012]\d\|3[01]\) \([01]\d\|2[0-3]\):[0-5]\d:\([0-5]\d\|60\)$', '%Y-%m-%d %H:%M:%S'->strftime()) call assert_fails('call strftime([])', 'E730:') call assert_fails('call strftime("%Y", [])', 'E745:') @@ -307,13 +307,19 @@ func Test_resolve_unix() call assert_equal('/', resolve('/')) endfunc +func s:normalize_fname(fname) + let ret = substitute(a:fname, '\', '/', 'g') + let ret = substitute(ret, '//', '/', 'g') + return ret->tolower() +endfunc + func Test_simplify() call assert_equal('', simplify('')) call assert_equal('/', simplify('/')) call assert_equal('/', simplify('/.')) call assert_equal('/', simplify('/..')) call assert_equal('/...', simplify('/...')) - call assert_equal('./dir/file', simplify('./dir/file')) + call assert_equal('./dir/file', './dir/file'->simplify()) call assert_equal('./dir/file', simplify('.///dir//file')) call assert_equal('./dir/file', simplify('./dir/./file')) call assert_equal('./file', simplify('./dir/../file')) @@ -346,7 +352,7 @@ func Test_setbufvar_options() wincmd h let wh = winheight(0) let dummy_buf = bufnr('dummy_buf2', v:true) - call setbufvar(dummy_buf, '&buftype', 'nofile') + eval 'nofile'->setbufvar(dummy_buf, '&buftype') execute 'belowright vertical split #' . dummy_buf call assert_equal(wh, winheight(0)) @@ -375,7 +381,7 @@ endfunc func Test_strpart() call assert_equal('de', strpart('abcdefg', 3, 2)) call assert_equal('ab', strpart('abcdefg', -2, 4)) - call assert_equal('abcdefg', strpart('abcdefg', -2)) + call assert_equal('abcdefg', 'abcdefg'->strpart(-2)) call assert_equal('fg', strpart('abcdefg', 5, 4)) call assert_equal('defg', strpart('abcdefg', 3)) @@ -469,7 +475,7 @@ func Test_toupper() \ toupper(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')) " Test with a few lowercase diacritics. - call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", toupper("aàáâãäåāăąǎǟǡả")) + call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", "aàáâãäåāăąǎǟǡả"->toupper()) call assert_equal("BḂḆ", toupper("bḃḇ")) call assert_equal("CÇĆĈĊČ", toupper("cçćĉċč")) call assert_equal("DĎĐḊḎḐ", toupper("dďđḋḏḑ")) @@ -532,6 +538,11 @@ func Test_toupper() call toupper("123\xC0\x80\xC0") endfunc +func Test_tr() + call assert_equal('foo', tr('bar', 'bar', 'foo')) + call assert_equal('zxy', 'cab'->tr('abc', 'xyz')) +endfunc + " Tests for the mode() function let current_modes = '' func Save_mode() @@ -809,11 +820,11 @@ endfunc func Test_stridx() call assert_equal(-1, stridx('', 'l')) call assert_equal(0, stridx('', '')) - call assert_equal(0, stridx('hello', '')) + call assert_equal(0, 'hello'->stridx('')) call assert_equal(-1, stridx('hello', 'L')) call assert_equal(2, stridx('hello', 'l', -1)) call assert_equal(2, stridx('hello', 'l', 0)) - call assert_equal(2, stridx('hello', 'l', 1)) + call assert_equal(2, 'hello'->stridx('l', 1)) call assert_equal(3, stridx('hello', 'l', 3)) call assert_equal(-1, stridx('hello', 'l', 4)) call assert_equal(-1, stridx('hello', 'l', 10)) @@ -826,7 +837,7 @@ func Test_strridx() call assert_equal(0, strridx('', '')) call assert_equal(5, strridx('hello', '')) call assert_equal(-1, strridx('hello', 'L')) - call assert_equal(3, strridx('hello', 'l')) + call assert_equal(3, 'hello'->strridx('l')) call assert_equal(3, strridx('hello', 'l', 10)) call assert_equal(3, strridx('hello', 'l', 3)) call assert_equal(2, strridx('hello', 'l', 2)) @@ -1219,7 +1230,7 @@ func Test_shellescape() let save_shell = &shell set shell=bash call assert_equal("'text'", shellescape('text')) - call assert_equal("'te\"xt'", shellescape('te"xt')) + call assert_equal("'te\"xt'", 'te"xt'->shellescape()) call assert_equal("'te'\\''xt'", shellescape("te'xt")) call assert_equal("'te%xt'", shellescape("te%xt")) @@ -1293,7 +1304,7 @@ endfunc func Test_trim() call assert_equal("Testing", trim(" \t\r\r\x0BTesting \t\n\r\n\t\x0B\x0B")) - call assert_equal("Testing", trim(" \t \r\r\n\n\x0BTesting \t\n\r\n\t\x0B\x0B")) + call assert_equal("Testing", " \t \r\r\n\n\x0BTesting \t\n\r\n\t\x0B\x0B"->trim()) call assert_equal("RESERVE", trim("xyz \twwRESERVEzyww \t\t", " wxyz\t")) call assert_equal("wRE \tSERVEzyww", trim("wRE \tSERVEzyww")) call assert_equal("abcd\t xxxx tail", trim(" \tabcd\t xxxx tail")) @@ -1330,7 +1341,7 @@ func Test_func_range_with_edit() " is invalid in that buffer. call writefile(['just one line'], 'Xfuncrange2') new - call setline(1, 10->range()) + eval 10->range()->setline(1) write Xfuncrange1 call assert_fails('5,8call EditAnotherFile()', 'E16:') @@ -1560,7 +1571,7 @@ func Test_bufadd_bufload() call assert_equal([''], getbufline(buf, 1, '$')) let curbuf = bufnr('') - call writefile(['some', 'text'], 'XotherName') + eval ['some', 'text']->writefile('XotherName') let buf = 'XotherName'->bufadd() call assert_notequal(0, buf) eval 'XotherName'->bufexists()->assert_equal(1) @@ -1592,6 +1603,10 @@ func Test_bufadd_bufload() endfunc func Test_readdir() + if isdirectory('Xdir') + call delete('Xdir', 'rf') + endif + call mkdir('Xdir') call writefile([], 'Xdir/foo.txt') call writefile([], 'Xdir/bar.txt') diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 6fd9477ce9..899eb530ec 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -651,6 +651,23 @@ func Test_1_highlight_Normalgroup_exists() endif endfunc +function Test_no_space_before_xxx() + " Note: we need to create this highlight group in the test because it does not exist in Neovim + execute('hi StatusLineTermNC ctermfg=green') + let l:org_columns = &columns + set columns=17 + let l:hi_StatusLineTermNC = join(split(execute('hi StatusLineTermNC'))) + call assert_match('StatusLineTermNC xxx', l:hi_StatusLineTermNC) + let &columns = l:org_columns +endfunction + +" Test for :highlight command errors +func Test_highlight_cmd_errors() + if has('gui_running') || has('nvim') + call assert_fails('hi ' .. repeat('a', 201) .. ' ctermfg=black', 'E1249:') + endif +endfunc + " Test for using RGB color values in a highlight group func Test_xxlast_highlight_RGB_color() CheckCanRunGui diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim index 8a1393a45d..f4ee539803 100644 --- a/src/nvim/testdir/test_listchars.vim +++ b/src/nvim/testdir/test_listchars.vim @@ -25,7 +25,7 @@ func Test_listchars() redraw! for i in range(1, 5) call cursor(i, 1) - call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) + call assert_equal([expected[i - 1]], ScreenLines(i, '$'->virtcol())) endfor set listchars-=trail:< @@ -112,7 +112,7 @@ func Test_listchars() " Test lead and trail normal ggdG - set listchars=eol:$ + set listchars=eol:$ " Accommodate Nvim default set listchars+=lead:>,trail:<,space:x set list @@ -142,7 +142,7 @@ func Test_listchars() " Test multispace normal ggdG - set listchars=eol:$ + set listchars=eol:$ " Accommodate Nvim default set listchars+=multispace:yYzZ set list @@ -286,6 +286,10 @@ func Test_listchars_unicode() call cursor(1, 1) call assert_equal(expected, ScreenLines(1, virtcol('$'))) + set listchars=eol:\\u21d4,space:\\u2423,multispace:≡\\u2262\\U00002263,nbsp:\\U00002260,tab:←↔\\u2192 + redraw! + call assert_equal(expected, ScreenLines(1, virtcol('$'))) + set listchars+=lead:⇨,trail:⇦ let expected = ['⇨⇨⇨⇨⇨⇨⇨⇨a←↔↔↔↔↔→b␣c≠d⇦⇦⇔'] redraw! @@ -301,7 +305,7 @@ func Test_listchars_invalid() enew! set ff=unix - set listchars=eol:$ + set listchars=eol:$ " Accommodate Nvim default set list set ambiwidth=double @@ -365,3 +369,138 @@ func Test_listchars_composing() enew! set listchars& ff& endfunction + +" Check for the value of the 'listchars' option +func s:CheckListCharsValue(expected) + call assert_equal(a:expected, &listchars) + call assert_equal(a:expected, getwinvar(0, '&listchars')) +endfunc + +" Test for using a window local value for 'listchars' +func Test_listchars_window_local() + %bw! + set list listchars& + let l:default_listchars = &listchars " Accommodate Nvim default + new + " set a local value for 'listchars' + setlocal listchars=tab:+-,eol:# + call s:CheckListCharsValue('tab:+-,eol:#') + " When local value is reset, global value should be used + setlocal listchars= + call s:CheckListCharsValue(l:default_listchars) " Accommodate Nvim default + " Use 'setlocal <' to copy global value + setlocal listchars=space:.,extends:> + setlocal listchars< + call s:CheckListCharsValue(l:default_listchars) " Accommodate Nvim default + " Use 'set <' to copy global value + setlocal listchars=space:.,extends:> + set listchars< + call s:CheckListCharsValue(l:default_listchars) " Accommodate Nvim default + " Changing global setting should not change the local setting + setlocal listchars=space:.,extends:> + setglobal listchars=tab:+-,eol:# + call s:CheckListCharsValue('space:.,extends:>') + " when split opening a new window, local value should be copied + split + call s:CheckListCharsValue('space:.,extends:>') + " clearing local value in one window should not change the other window + set listchars& + call s:CheckListCharsValue(l:default_listchars) " Accommodate Nvim default + close + call s:CheckListCharsValue('space:.,extends:>') + + " use different values for 'listchars' items in two different windows + call setline(1, ["\t one two "]) + setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# + split + setlocal listchars=tab:[.],lead:#,space:_,trail:.,eol:& + split + set listchars=tab:+-+,lead:^,space:>,trail:<,eol:% + call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + " changing the global setting should not change the local value + setglobal listchars=tab:[.],lead:#,space:_,trail:.,eol:& + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + set listchars< + call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$'))) + + " Using setglobal in a window with local setting should not affect the + " window. But should impact other windows using the global setting. + enew! | only + call setline(1, ["\t one two "]) + set listchars=tab:[.],lead:#,space:_,trail:.,eol:& + split + setlocal listchars=tab:+-+,lead:^,space:>,trail:<,eol:% + split + setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# + setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$ + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$'))) + + " Setting the global setting to the default value should not impact a window + " using a local setting. + split + setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# + setglobal listchars=eol:$ " Accommodate Nvim default + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['^I one two $'], ScreenLines(1, virtcol('$'))) + + " Setting the local setting to the default value should not impact a window + " using a global setting. + set listchars=tab:{.},lead:-,space:=,trail:#,eol:$ + split + setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + setlocal listchars=eol:$ " Accommodate Nvim default + call assert_equal(['^I one two $'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$'))) + + " Using set in a window with a local setting should change it to use the + " global setting and also impact other windows using the global setting. + split + setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + set listchars=tab:+-+,lead:^,space:>,trail:<,eol:% + call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) + + " Setting invalid value for a local setting should not impact the local and + " global settings. + split + setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# + let cmd = 'setlocal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x' + call assert_fails(cmd, 'E474:') + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) + + " Setting invalid value for a global setting should not impact the local and + " global settings. + split + setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:# + let cmd = 'setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x' + call assert_fails(cmd, 'E474:') + call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$'))) + close + call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$'))) + + " Closing window with local lcs-multispace should not cause a memory leak. + setlocal listchars=multispace:---+ + split + call s:CheckListCharsValue('multispace:---+') + close + + %bw! + set list& listchars& +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim index e0518de3c2..2fda12d8b4 100644 --- a/src/nvim/testdir/test_listlbr.vim +++ b/src/nvim/testdir/test_listlbr.vim @@ -43,6 +43,7 @@ endfunc func Test_linebreak_with_list() throw 'skipped: Nvim does not support enc=latin1' + set listchars= call s:test_windows('setl ts=4 sbr=+ list listchars=') call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ") let lines = s:screen_lines([1, 4], winwidth(0)) @@ -54,6 +55,7 @@ func Test_linebreak_with_list() \ ] call s:compare_lines(expect, lines) call s:close_windows() + set listchars&vim endfunc func Test_linebreak_with_nolist() diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 2fd82a4b6d..b3035d73ce 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -227,7 +227,7 @@ func Test_getmarklist() call cursor(2, 2) normal mr call assert_equal({'mark' : "'r", 'pos' : [bufnr(), 2, 2, 0]}, - \ getmarklist(bufnr())[0]) - call assert_equal([], getmarklist({})) + \ bufnr()->getmarklist()[0]) + call assert_equal([], {}->getmarklist()) close! endfunc diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim index 34c8c49dd5..7bfac13ad8 100644 --- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim +++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim @@ -6,7 +6,7 @@ endif function! s:screenline(lnum) abort let line = [] for c in range(1, winwidth(0)) - call add(line, nr2char(screenchar(a:lnum, c))) + call add(line, nr2char(a:lnum->screenchar(c))) endfor return s:trim(join(line, '')) endfunction diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 08586dffe1..2140fe21ea 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -87,7 +87,7 @@ func Test_echoerr() if has('float') call assert_equal("\n1.23 IgNoRe", execute(':echoerr 1.23 "IgNoRe"')) endif - call test_ignore_error('<lambda>') + eval '<lambda>'->test_ignore_error() call assert_match("function('<lambda>\\d*')", execute(':echoerr {-> 1234}')) call test_ignore_error('RESET') endfunc diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index c96c6a9678..057895047d 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -156,8 +156,7 @@ func Test_mksession_zero_winheight() wincmd _ mksession! Xtest_mks_zero set winminheight& - " let text = readfile('Xtest_mks_zero')->join() - let text = join(readfile('Xtest_mks_zero')) + let text = readfile('Xtest_mks_zero')->join() call delete('Xtest_mks_zero') close " check there is no divide by zero diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 41c689849b..7d9cada074 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -235,8 +235,7 @@ func Test_set_completion() call feedkeys(":set filetype=sshdconfi\<Tab>\<C-B>\"\<CR>", 'xt') call assert_equal('"set filetype=sshdconfig', @:) call feedkeys(":set filetype=a\<C-A>\<C-B>\"\<CR>", 'xt') - " call assert_equal('"set filetype=' .. getcompletion('a*', 'filetype')->join(), @:) - call assert_equal('"set filetype=' .. join(getcompletion('a*', 'filetype')), @:) + call assert_equal('"set filetype=' .. getcompletion('a*', 'filetype')->join(), @:) endfunc func Test_set_errors() diff --git a/src/nvim/testdir/test_prompt_buffer.vim b/src/nvim/testdir/test_prompt_buffer.vim index 3da46eb1a6..8f94a8572b 100644 --- a/src/nvim/testdir/test_prompt_buffer.vim +++ b/src/nvim/testdir/test_prompt_buffer.vim @@ -41,6 +41,10 @@ func WriteScript(name) \ ' set nomodified', \ 'endfunc', \ '', + \ 'func SwitchWindows()', + \ ' call timer_start(0, {-> execute("wincmd p|wincmd p", "")})', + \ 'endfunc', + \ '', \ 'call setline(1, "other buffer")', \ 'set nomodified', \ 'new', @@ -89,9 +93,12 @@ func Test_prompt_editing() call term_sendkeys(buf, left . left . left . bs . '-') call WaitForAssert({-> assert_equal('cmd: -hel', term_getline(buf, 1))}) + call term_sendkeys(buf, "\<C-O>lz") + call WaitForAssert({-> assert_equal('cmd: -hzel', term_getline(buf, 1))}) + let end = "\<End>" call term_sendkeys(buf, end . "x") - call WaitForAssert({-> assert_equal('cmd: -helx', term_getline(buf, 1))}) + call WaitForAssert({-> assert_equal('cmd: -hzelx', term_getline(buf, 1))}) call term_sendkeys(buf, "\<C-U>exit\<CR>") call WaitForAssert({-> assert_equal('other buffer', term_getline(buf, 1))}) @@ -100,6 +107,28 @@ func Test_prompt_editing() call delete(scriptName) endfunc +func Test_prompt_switch_windows() + throw 'skipped: TODO' + call CanTestPromptBuffer() + let scriptName = 'XpromptSwitchWindows' + call WriteScript(scriptName) + + let buf = RunVimInTerminal('-S ' . scriptName, {'rows': 12}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 12))}) + + call term_sendkeys(buf, "\<C-O>:call SwitchWindows()\<CR>") + call term_wait(buf, 50) + call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 12))}) + + call term_sendkeys(buf, "\<Esc>") + call term_wait(buf, 50) + call WaitForAssert({-> assert_match('^ *$', term_getline(buf, 12))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + func Test_prompt_garbage_collect() func MyPromptCallback(x, text) " NOP @@ -126,6 +155,14 @@ func Test_prompt_garbage_collect() bwipe! endfunc +func Test_prompt_backspace() + new + set buftype=prompt + call feedkeys("A123456\<Left>\<BS>\<Esc>", 'xt') + call assert_equal('% 12346', getline(1)) + bwipe! +endfunc + " Test for editing the prompt buffer func Test_prompt_buffer_edit() new @@ -145,10 +182,9 @@ func Test_prompt_buffer_edit() call assert_beeps("normal! \<C-X>") " pressing CTRL-W in the prompt buffer should trigger the window commands call assert_equal(1, winnr()) - " In Nvim, CTRL-W commands aren't usable from insert mode in a prompt buffer - " exe "normal A\<C-W>\<C-W>" - " call assert_equal(2, winnr()) - " wincmd w + exe "normal A\<C-W>\<C-W>" + call assert_equal(2, winnr()) + wincmd w close! call assert_equal(0, prompt_setprompt([], '')) endfunc @@ -165,9 +201,7 @@ func Test_prompt_buffer_getbufinfo() call assert_equal('This is a test: ', prompt_getprompt('%')) call prompt_setprompt( bufnr( '%' ), '' ) - " Nvim doesn't support method call syntax yet. - " call assert_equal('', '%'->prompt_getprompt()) - call assert_equal('', prompt_getprompt('%')) + call assert_equal('', '%'->prompt_getprompt()) call prompt_setprompt( bufnr( '%' ), 'Another: ' ) call assert_equal('Another: ', prompt_getprompt('%')) @@ -189,4 +223,38 @@ func Test_prompt_buffer_getbufinfo() %bwipe! endfunc +function! Test_prompt_while_writing_to_hidden_buffer() + throw 'skipped: TODO' + call CanTestPromptBuffer() + CheckUnix + + " Make a job continuously write to a hidden buffer, check that the prompt + " buffer is not affected. + let scriptName = 'XpromptscriptHiddenBuf' + let script =<< trim END + set buftype=prompt + call prompt_setprompt( bufnr(), 'cmd:' ) + let job = job_start(['/bin/sh', '-c', + \ 'while true; + \ do echo line; + \ sleep 0.1; + \ done'], #{out_io: 'buffer', out_name: ''}) + startinsert + END + eval script->writefile(scriptName) + + let buf = RunVimInTerminal('-S ' .. scriptName, {}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:test', term_getline(buf, 1))}) + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:testtest', term_getline(buf, 1))}) + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:testtesttest', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 15745d5619..f42b177c50 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -39,7 +39,7 @@ func Test_put_lines() call assert_equal(['Line 3', '', 'Line 1', 'Line2'], getline(1, '$')) " clean up bw! - call setreg('a', a[0], a[1]) + eval a[0]->setreg('a', a[1]) endfunc func Test_put_expr() @@ -111,3 +111,16 @@ func Test_put_p_indent_visual() call assert_equal('select that text', getline(2)) bwipe! endfunc + +func Test_multibyte_op_end_mark() + new + call setline(1, 'тест') + normal viwdp + call assert_equal([0, 1, 7, 0], getpos("'>")) + call assert_equal([0, 1, 7, 0], getpos("']")) + + normal Vyp + call assert_equal([0, 1, 2147483647, 0], getpos("'>")) + call assert_equal([0, 2, 7, 0], getpos("']")) + bwipe! + endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 8c6ce63ade..6db679c5f9 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -811,7 +811,7 @@ func Test_locationlist() " NOTE: problem 1: " intentionally not setting 'lnum' so that the quickfix entries are not " valid - call setloclist(0, qflist, ' ') + eval qflist->setloclist(0, ' ') endfor " Test A @@ -1699,7 +1699,7 @@ endfunc func Test_setqflist_invalid_nr() " The following command used to crash Vim - call setqflist([], ' ', {'nr' : $XXX_DOES_NOT_EXIST}) + eval []->setqflist(' ', {'nr' : $XXX_DOES_NOT_EXIST}) endfunc func Test_setqflist_user_sets_buftype() @@ -3653,6 +3653,9 @@ func Xqftick_tests(cchar) \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'r') call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick) + if isdirectory("Xone") + call delete("Xone", 'rf') + endif call writefile(["F8:80:L80", "F8:81:L81"], "Xone") Xfile Xone call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick) diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index fd8653a2eb..84a5aca3d5 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -43,6 +43,10 @@ func Test_yank_shows_register() endfunc func Test_display_registers() + " Disable clipboard + let save_clipboard = get(g:, 'clipboard', {}) + let g:clipboard = {} + e file1 e file2 call setline(1, ['foo', 'bar']) @@ -78,6 +82,7 @@ func Test_display_registers() \ . ' c ": ls', a) bwipe! + let g:clipboard = save_clipboard endfunc func Test_recording_status_in_ex_line() diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim index 1a274d1fec..1fbf3392d9 100644 --- a/src/nvim/testdir/test_ruby.vim +++ b/src/nvim/testdir/test_ruby.vim @@ -60,7 +60,7 @@ func Test_ruby_set_cursor() " Check that movement after setting cursor position keeps current column. normal j call assert_equal([2, 6], [line('.'), col('.')]) - call assert_equal([2, 5], rubyeval('$curwin.cursor')) + call assert_equal([2, 5], '$curwin.cursor'->rubyeval()) " call assert_fails('ruby $curwin.cursor = [1]', " \ 'ArgumentError: array length must be 2') diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 7570049e7c..c796f1f676 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -1315,7 +1315,7 @@ func Test_search_match_at_curpos() normal gg - call search('foobar', 'c') + eval 'foobar'->search('c') call assert_equal([1, 1], [line('.'), col('.')]) normal j @@ -1354,6 +1354,41 @@ func Test_search_display_pattern() endif endfunc +func Test_searchdecl() + let lines =<< trim END + int global; + + func() + { + int global; + if (cond) { + int local; + } + int local; + // comment + } + END + new + call setline(1, lines) + 10 + call assert_equal(0, searchdecl('local', 0, 0)) + call assert_equal(7, getcurpos()[1]) + + 10 + call assert_equal(0, 'local'->searchdecl(0, 1)) + call assert_equal(9, getcurpos()[1]) + + 10 + call assert_equal(0, searchdecl('global')) + call assert_equal(5, getcurpos()[1]) + + 10 + call assert_equal(0, searchdecl('global', 1)) + call assert_equal(1, getcurpos()[1]) + + bwipe! +endfunc + func Test_search_special() " this was causing illegal memory access and an endless loop set t_PE= diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim index dd4707977e..76d1306836 100644 --- a/src/nvim/testdir/test_sha256.vim +++ b/src/nvim/testdir/test_sha256.vim @@ -6,17 +6,17 @@ endif function Test_sha256() " test for empty string: - call assert_equal(sha256(""), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') + call assert_equal('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', sha256("")) "'test for 1 char: - call assert_equal(sha256("a"), 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb') + call assert_equal('ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', sha256("a")) " "test for 3 chars: - call assert_equal(sha256("abc"), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + call assert_equal('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', "abc"->sha256()) " test for contains meta char: - call assert_equal(sha256("foo\nbar"), '807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776') + call assert_equal('807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776', sha256("foo\nbar")) " test for contains non-ascii char: - call assert_equal(sha256("\xde\xad\xbe\xef"), '5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953') + call assert_equal('5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953', sha256("\xde\xad\xbe\xef")) endfunction diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 9753100375..799e6cb57b 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -15,13 +15,13 @@ func Test_sign() " the icon name when listing signs. sign define Sign1 text=x - call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png') + call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search icon=../../pixmaps/stock_vim_find_help.png') " Test listing signs. let a=execute('sign list') call assert_match('^\nsign Sign1 text=x \nsign Sign2 ' . \ 'icon=../../pixmaps/stock_vim_find_help.png .*text=xy ' . - \ 'linehl=Error texthl=Title$', a) + \ 'linehl=Error texthl=Title culhl=Search$', a) let a=execute('sign list Sign1') call assert_equal("\nsign Sign1 text=x ", a) @@ -126,6 +126,30 @@ func Test_sign() " call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:') + " an empty highlight argument for an existing sign clears it + sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl + let sl = sign_getdefined('SignY')[0] + call assert_equal('TextHl', sl.texthl) + call assert_equal('CulHl', sl.culhl) + call assert_equal('LineHl', sl.linehl) + + sign define SignY texthl= culhl=CulHl linehl=LineHl + let sl = sign_getdefined('SignY')[0] + call assert_false(has_key(sl, 'texthl')) + call assert_equal('CulHl', sl.culhl) + call assert_equal('LineHl', sl.linehl) + + sign define SignY linehl= + let sl = sign_getdefined('SignY')[0] + call assert_false(has_key(sl, 'linehl')) + call assert_equal('CulHl', sl.culhl) + + sign define SignY culhl= + let sl = sign_getdefined('SignY')[0] + call assert_false(has_key(sl, 'culhl')) + + sign undefine SignY + " define sign with whitespace sign define Sign4 text=\ X linehl=Comment sign undefine Sign4 @@ -392,25 +416,27 @@ func Test_sign_funcs() call sign_undefine() " Tests for sign_define() - let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} - call assert_equal(0, sign_define("sign1", attr)) + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error', + \ 'culhl': 'Visual'} + call assert_equal(0, "sign1"->sign_define(attr)) call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', - \ 'linehl' : 'Search', 'text' : '=>'}], sign_getdefined()) + \ 'linehl' : 'Search', 'culhl': 'Visual', 'text' : '=>'}], + \ sign_getdefined()) " Define a new sign without attributes and then update it call sign_define("sign2") let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange', - \ 'icon' : 'sign2.ico'} + \ 'culhl': 'DiffDelete', 'icon' : 'sign2.ico'} call Sign_define_ignore_error("sign2", attr) call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange', - \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}], - \ sign_getdefined("sign2")) + \ 'linehl' : 'DiffAdd', 'culhl' : 'DiffDelete', 'text' : '!!', + \ 'icon' : 'sign2.ico'}], "sign2"->sign_getdefined()) " Test for a sign name with digits call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'})) call assert_equal([{'name' : '2', 'linehl' : 'StatusLine'}], \ sign_getdefined(0002)) - call sign_undefine(0002) + eval 0002->sign_undefine() " Tests for invalid arguments to sign_define() call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:') @@ -434,7 +460,7 @@ func Test_sign_funcs() call assert_equal([{'bufnr' : bufnr(''), 'signs' : \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', \ 'priority' : 10}]}], - \ sign_getplaced('%', {'lnum' : 20})) + \ '%'->sign_getplaced({'lnum' : 20})) call assert_equal([{'bufnr' : bufnr(''), 'signs' : \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', \ 'priority' : 10}]}], @@ -490,10 +516,10 @@ func Test_sign_funcs() \ 'E745:') " Tests for sign_unplace() - call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) + eval 20->sign_place('', 'sign2', 'Xsign', {"lnum" : 30}) call assert_equal(0, sign_unplace('', \ {'id' : 20, 'buffer' : 'Xsign'})) - call assert_equal(-1, sign_unplace('', + call assert_equal(-1, ''->sign_unplace( \ {'id' : 30, 'buffer' : 'Xsign'})) call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) call assert_fails("call sign_unplace('', @@ -1693,7 +1719,7 @@ func Test_sign_jump_func() let r = sign_jump(5, '', 'foo') call assert_equal(2, r) call assert_equal(2, line('.')) - let r = sign_jump(6, 'g1', 'foo') + let r = 6->sign_jump('g1', 'foo') call assert_equal(5, r) call assert_equal(5, line('.')) let r = sign_jump(5, '', 'bar') @@ -1921,8 +1947,7 @@ func Test_sign_funcs_multi() \ 'group' : 'g1', 'priority' : 10}], s[0].signs) " Change an existing sign without specifying the group - call assert_equal([5], sign_placelist([ - \ {'id' : 5, 'name' : 'sign1', 'buffer' : 'Xsign'}])) + call assert_equal([5], [{'id' : 5, 'name' : 'sign1', 'buffer' : 'Xsign'}]->sign_placelist()) let s = sign_getplaced('Xsign', {'id' : 5, 'group' : ''}) call assert_equal([{'id' : 5, 'name' : 'sign1', 'lnum' : 11, \ 'group' : '', 'priority' : 10}], s[0].signs) @@ -1955,7 +1980,7 @@ func Test_sign_funcs_multi() \ {'id' : 1, 'group' : 'g1'}, {'id' : 1, 'group' : 'g2'}])) " Invalid arguments - call assert_equal([], sign_unplacelist([])) + call assert_equal([], []->sign_unplacelist()) call assert_fails('call sign_unplacelist({})', "E714:") call assert_fails('call sign_unplacelist([[]])', "E715:") call assert_fails('call sign_unplacelist(["abc"])', "E715:") diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index e525d06ea2..cf0faeee31 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -77,7 +77,7 @@ func Test_spellbadword() set spell call assert_equal(['bycycle', 'bad'], spellbadword('My bycycle.')) - call assert_equal(['another', 'caps'], spellbadword('A sentence. another sentence')) + call assert_equal(['another', 'caps'], 'A sentence. another sentence'->spellbadword()) call assert_equal(['TheCamelWord', 'bad'], spellbadword('TheCamelWord asdf')) set spelloptions=camel @@ -407,7 +407,7 @@ func Test_zz_basic() \ ) call assert_equal("gebletegek", soundfold('goobledygoook')) - call assert_equal("kepereneven", soundfold('kóopërÿnôven')) + call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold()) call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale')) endfunc @@ -711,7 +711,7 @@ func TestGoodBadBase() break endif let prevbad = bad - let lst = spellsuggest(bad, 3) + let lst = bad->spellsuggest(3) normal mm call add(result, [bad, lst]) diff --git a/src/nvim/testdir/test_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim index cafdb97f28..3d159f3352 100644 --- a/src/nvim/testdir/test_spell_utf8.vim +++ b/src/nvim/testdir/test_spell_utf8.vim @@ -512,8 +512,7 @@ func TestGoodBadBase() break endif let prevbad = bad - " let lst = bad->spellsuggest(3) - let lst = spellsuggest(bad, 3) + let lst = bad->spellsuggest(3) normal mm call add(result, [bad, lst]) @@ -552,8 +551,7 @@ func Test_spell_basic() \ ) call assert_equal("gebletegek", soundfold('goobledygoook')) - " call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold()) - call assert_equal("kepereneven", soundfold('kóopërÿnôven')) + call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold()) call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale')) endfunc diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index b140077111..d830f5216d 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -905,15 +905,13 @@ func Test_not_a_term() " This will take 2 seconds because of the missing --not-a-term let cmd = GetVimProg() .. ' --cmd quit ' .. redir exe "silent !" . cmd - " call assert_match("\<Esc>", readfile('Xvimout')->join()) - call assert_match("\<Esc>", join(readfile('Xvimout'))) + call assert_match("\<Esc>", readfile('Xvimout')->join()) call delete('Xvimout') " With --not-a-term there are no escape sequences. let cmd = GetVimProg() .. ' --not-a-term --cmd quit ' .. redir exe "silent !" . cmd - " call assert_notmatch("\<Esc>", readfile('Xvimout')->join()) - call assert_notmatch("\<Esc>", join(readfile('Xvimout'))) + call assert_notmatch("\<Esc>", readfile('Xvimout')->join()) call delete('Xvimout') endfunc diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index e7f9bb76f2..20b760ac15 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -137,7 +137,7 @@ func Test_substitute_repeat() " This caused an invalid memory access. split Xfile s/^/x - call feedkeys("Qsc\<CR>y", 'tx') + call feedkeys("gQsc\<CR>y", 'tx') bwipe! endfunc @@ -547,7 +547,7 @@ func Test_sub_replace_5() \ substitute('A123456789', \ 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', \ '\=string([submatch(0, 1), submatch(9, 1), ' . - \ 'submatch(8, 1), submatch(7, 1), submatch(6, 1), ' . + \ 'submatch(8, 1), 7->submatch(1), submatch(6, 1), ' . \ 'submatch(5, 1), submatch(4, 1), submatch(3, 1), ' . \ 'submatch(2, 1), submatch(1, 1)])', \ '')) @@ -752,8 +752,7 @@ endfunc func Test_submatch_list_concatenate() let pat = 'A\(.\)' let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])} - " call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]") - call assert_equal(substitute('A1', pat, Rep, ''), "[['A1'], ['1']]") + call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]") endfunc func Test_substitute_skipped_range() diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index e3101d4e44..b3018b2b0d 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -113,7 +113,7 @@ func Test_swapinfo() w let fname = s:swapname() call assert_match('Xswapinfo', fname) - let info = swapinfo(fname) + let info = fname->swapinfo() let ver = printf('VIM %d.%d', v:version / 100, v:version % 100) call assert_equal(ver, info.version) @@ -155,7 +155,7 @@ func Test_swapname() let buf = bufnr('%') let expected = s:swapname() wincmd p - call assert_equal(expected, swapname(buf)) + call assert_equal(expected, buf->swapname()) new Xtest3 setlocal noswapfile diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 914d9c2782..757866f5dc 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -30,23 +30,17 @@ func AssertHighlightGroups(lnum, startcol, expected, trans = 1, msg = "") " If groups are provided as a string, each character is assumed to be a " group and spaces represent no group, useful for visually describing tests. let l:expectedGroups = type(a:expected) == v:t_string - "\ ? a:expected->split('\zs')->map({_, v -> trim(v)}) - \ ? map(split(a:expected, '\zs'), {_, v -> trim(v)}) + \ ? a:expected->split('\zs')->map({_, v -> trim(v)}) \ : a:expected let l:errors = 0 - " let l:msg = (a:msg->empty() ? "" : a:msg .. ": ") - let l:msg = (empty(a:msg) ? "" : a:msg .. ": ") + let l:msg = (a:msg->empty() ? "" : a:msg .. ": ") \ .. "Wrong highlight group at " .. a:lnum .. "," - " for l:i in range(a:startcol, a:startcol + l:expectedGroups->len() - 1) - " let l:errors += synID(a:lnum, l:i, a:trans) - " \ ->synIDattr("name") - " \ ->assert_equal(l:expectedGroups[l:i - 1], - for l:i in range(a:startcol, a:startcol + len(l:expectedGroups) - 1) - let l:errors += - \ assert_equal(synIDattr(synID(a:lnum, l:i, a:trans), "name"), - \ l:expectedGroups[l:i - 1], - \ l:msg .. l:i) + for l:i in range(a:startcol, a:startcol + l:expectedGroups->len() - 1) + let l:errors += synID(a:lnum, l:i, a:trans) + \ ->synIDattr("name") + \ ->assert_equal(l:expectedGroups[l:i - 1], + \ l:msg .. l:i) endfor endfunc diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 7b8ee778cc..1858b48807 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -121,8 +121,7 @@ func Test_system_with_shell_quote() let msg = printf('shell=%s shellxquote=%s', &shell, &shellxquote) try - " let out = 'echo 123'->system() - let out = system('echo 123') + let out = 'echo 123'->system() catch call assert_report(printf('%s: %s', msg, v:exception)) continue diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index b261b96c3b..9869dc7590 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -35,7 +35,7 @@ function Test_tabpage() tabnew tabfirst call settabvar(2, 'val_num', 100) - call settabvar(2, 'val_str', 'SetTabVar test') + eval 'SetTabVar test'->settabvar(2, 'val_str') call settabvar(2, 'val_list', ['red', 'blue', 'green']) " call assert_true(gettabvar(2, 'val_num') == 100 && gettabvar(2, 'val_str') == 'SetTabVar test' && gettabvar(2, 'val_list') == ['red', 'blue', 'green']) @@ -184,7 +184,7 @@ function Test_tabpage_with_autocmd() let s:li = split(join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")')), '\s\+') call assert_equal(['a', 'a'], s:li) let s:li = [] - C call map(copy(winr), 'settabwinvar('.tabn.', v:val, ''a'', v:val*2)') + C call map(copy(winr), '(v:val*2)->settabwinvar(' .. tabn .. ', v:val, ''a'')') let s:li = split(join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")')), '\s\+') call assert_equal(['2', '4'], s:li) diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 15182893e9..2aa04df42a 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -351,7 +351,7 @@ func Test_getsettagstack() " Try to set current index to invalid values call settagstack(1, {'curidx' : -1}) call assert_equal(1, gettagstack().curidx) - call settagstack(1, {'curidx' : 50}) + eval {'curidx' : 50}->settagstack(1) call assert_equal(4, gettagstack().curidx) " Try pushing invalid items onto the stack diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim index e830813081..e11815ff33 100644 --- a/src/nvim/testdir/test_taglist.vim +++ b/src/nvim/testdir/test_taglist.vim @@ -14,7 +14,7 @@ func Test_taglist() split Xtext call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo"), {i, v -> v.name})) - call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xtext"), {i, v -> v.name})) + call assert_equal(['FFoo', 'BFoo'], map("Foo"->taglist("Xtext"), {i, v -> v.name})) call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xfoo"), {i, v -> v.name})) call assert_equal(['BFoo', 'FFoo'], map(taglist("Foo", "Xbar"), {i, v -> v.name})) diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim index c259453b5e..2b6bb8b302 100644 --- a/src/nvim/testdir/test_textobjects.vim +++ b/src/nvim/testdir/test_textobjects.vim @@ -421,4 +421,36 @@ func Test_textobj_quote() close! endfunc +" Test for i(, i<, etc. when cursor is in front of a block +func Test_textobj_find_paren_forward() + new + + " i< and a> when cursor is in front of a block + call setline(1, '#include <foo.h>') + normal 0yi< + call assert_equal('foo.h', @") + normal 0ya> + call assert_equal('<foo.h>', @") + + " 2i(, 3i( in front of a block enters second/third nested '(' + call setline(1, 'foo (bar (baz (quux)))') + normal 0yi) + call assert_equal('bar (baz (quux))', @") + normal 02yi) + call assert_equal('baz (quux)', @") + normal 03yi) + call assert_equal('quux', @") + + " 3i( in front of a block doesn't enter third but un-nested '(' + call setline(1, 'foo (bar (baz) (quux))') + normal 03di) + call assert_equal('foo (bar (baz) (quux))', getline(1)) + normal 02di) + call assert_equal('foo (bar () (quux))', getline(1)) + normal 0di) + call assert_equal('foo ()', getline(1)) + + close! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index ceaa5de92b..aae315b2c5 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -77,7 +77,7 @@ endfunc func Test_info() let id = timer_start(1000, 'MyHandler') - let info = timer_info(id) + let info = id->timer_info() call assert_equal(id, info[0]['id']) call assert_equal(1000, info[0]['time']) call assert_equal("function('MyHandler')", string(info[0]['callback'])) @@ -113,7 +113,7 @@ func Test_paused() let info = timer_info(id) call assert_equal(0, info[0]['paused']) - call timer_pause(id, 1) + eval id->timer_pause(1) let info = timer_info(id) call assert_equal(1, info[0]['paused']) sleep 200m @@ -148,7 +148,7 @@ func Test_delete_myself() endfunc func StopTimer1(timer) - let g:timer2 = timer_start(10, 'StopTimer2') + let g:timer2 = 10->timer_start('StopTimer2') " avoid maxfuncdepth error call timer_pause(g:timer1, 1) sleep 40m @@ -239,7 +239,7 @@ func FeedAndPeek(timer) endfunc func Interrupt(timer) - " call test_feedinput("\<C-C>") + " eval "\<C-C>"->test_feedinput() call nvim_input("\<C-C>") endfunc @@ -251,7 +251,7 @@ func Test_peek_and_get_char() let intr = timer_start(100, 'Interrupt') let c = getchar() call assert_equal(char2nr('a'), c) - call timer_stop(intr) + eval intr->timer_stop() endfunc func Test_getchar_zero() @@ -279,7 +279,7 @@ func Test_ex_mode() endfunc let timer = timer_start(40, function('g:Foo'), {'repeat':-1}) " This used to throw error E749. - exe "normal Qsleep 100m\rvi\r" + exe "normal gQsleep 100m\rvi\r" call timer_stop(timer) endfunc diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index c7dcaa0f36..30e00e7ad4 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -490,7 +490,7 @@ funct Test_undofile() call delete('Xundodir', 'd') " Test undofile() with 'undodir' set to a non-existing directory. - " call assert_equal('', undofile('Xundofoo')) + " call assert_equal('', 'Xundofoo'->undofile()) if isdirectory('/tmp') set undodir=/tmp diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index da72da087f..0818c2e4b0 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -17,7 +17,7 @@ func Test_strchars() let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]] for i in range(len(inp)) call assert_equal(exp[i][0], strchars(inp[i])) - call assert_equal(exp[i][1], strchars(inp[i], 0)) + call assert_equal(exp[i][1], inp[i]->strchars(0)) call assert_equal(exp[i][2], strchars(inp[i], 1)) endfor endfunc @@ -69,7 +69,7 @@ func Test_screenchar_utf8() call setline(1, ["ABC\u0308"]) redraw call assert_equal([0x0041], screenchars(1, 1)) - call assert_equal([0x0042], screenchars(1, 2)) + call assert_equal([0x0042], 1->screenchars(2)) call assert_equal([0x0043, 0x0308], screenchars(1, 3)) call assert_equal("A", screenstring(1, 1)) call assert_equal("B", screenstring(1, 2)) diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index 2fbf130345..46e0d62313 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -330,7 +330,7 @@ func Test_vartabs_shiftwidth() let lines = ScreenLines([1, 2], winwidth(0)) call s:compare_lines(expect2, lines) call assert_equal(20, shiftwidth(virtcol('.')-2)) - call assert_equal(30, shiftwidth(virtcol('.'))) + call assert_equal(30, virtcol('.')->shiftwidth()) norm! $>> let expect3 = [' ', ' x ', '~ '] let lines = ScreenLines([1, 3], winwidth(0)) diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 039de0c623..a200bf7d42 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -72,7 +72,7 @@ endfunc func Test_window_quit() e Xa split Xb - call assert_equal(2, winnr('$')) + call assert_equal(2, '$'->winnr()) call assert_equal('Xb', bufname(winbufnr(1))) call assert_equal('Xa', bufname(winbufnr(2))) @@ -88,7 +88,7 @@ func Test_window_horizontal_split() 3wincmd s call assert_equal(2, winnr('$')) call assert_equal(3, winheight(0)) - call assert_equal(winwidth(1), winwidth(2)) + call assert_equal(winwidth(1), 2->winwidth()) call assert_fails('botright topleft wincmd s', 'E442:') bw @@ -267,7 +267,7 @@ func Test_window_height() wincmd + call assert_equal(wh1, winheight(1)) - call assert_equal(wh2, winheight(2)) + call assert_equal(wh2, 2->winheight()) 2wincmd _ call assert_equal(2, winheight(1)) @@ -452,7 +452,7 @@ func Test_window_newtab() wincmd T call assert_equal(2, tabpagenr('$')) call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)')) - call assert_equal(['Xc' ], map(tabpagebuflist(2), 'bufname(v:val)')) + call assert_equal(['Xc' ], map(2->tabpagebuflist(), 'bufname(v:val)')) %bw! endfunc @@ -577,8 +577,11 @@ endfunc function! Fun_RenewFile() " Need to wait a bit for the timestamp to be older. - sleep 2 - silent execute '!echo "1" > tmp.txt' + let old_ftime = getftime("tmp.txt") + while getftime("tmp.txt") == old_ftime + sleep 100m + silent execute '!echo "1" > tmp.txt' + endwhile sp wincmd p edit! tmp.txt @@ -814,13 +817,25 @@ func Test_winnr() tabnew call assert_equal(8, tabpagewinnr(1, 'j')) - call assert_equal(2, tabpagewinnr(1, 'k')) + call assert_equal(2, 1->tabpagewinnr('k')) call assert_equal(4, tabpagewinnr(1, 'h')) call assert_equal(6, tabpagewinnr(1, 'l')) only | tabonly endfunc +func Test_winrestview() + split runtest.vim + normal 50% + let view = winsaveview() + close + split runtest.vim + eval view->winrestview() + call assert_equal(view, winsaveview()) + + bwipe! +endfunc + func Test_win_splitmove() edit a leftabove split b diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim index d10d831650..8bf4ede350 100644 --- a/src/nvim/testdir/test_window_id.vim +++ b/src/nvim/testdir/test_window_id.vim @@ -67,7 +67,7 @@ func Test_win_getid() call win_gotoid(id2) call assert_equal("two", expand("%")) - call win_gotoid(id4) + eval id4->win_gotoid() call assert_equal("four", expand("%")) call win_gotoid(id1) call assert_equal("one", expand("%")) @@ -75,17 +75,17 @@ func Test_win_getid() call assert_equal("five", expand("%")) call assert_equal(0, win_id2win(9999)) - call assert_equal(nr5, win_id2win(id5)) + call assert_equal(nr5, id5->win_id2win()) call assert_equal(0, win_id2win(id1)) tabnext call assert_equal(nr1, win_id2win(id1)) call assert_equal([0, 0], win_id2tabwin(9999)) - call assert_equal([1, nr2], win_id2tabwin(id2)) + call assert_equal([1, nr2], id2->win_id2tabwin()) call assert_equal([2, nr4], win_id2tabwin(id4)) call assert_equal([], win_findbuf(9999)) - call assert_equal([id2], win_findbuf(bufnr2)) + call assert_equal([id2], bufnr2->win_findbuf()) call win_gotoid(id5) split call assert_equal(sort([id5, win_getid()]), sort(win_findbuf(bufnr5))) @@ -98,7 +98,7 @@ func Test_win_getid_curtab() tabfirst copen only - call assert_equal(win_getid(1), win_getid(1, 1)) + call assert_equal(win_getid(1), 1->win_getid( 1)) tabclose! endfunc @@ -120,4 +120,11 @@ func Test_winlayout() call assert_equal(['col', [['leaf', w3], ['row', [['leaf', w4], ['leaf', w2]]], ['leaf', w1]]], winlayout()) only! + + let w1 = win_getid() + call assert_equal(['leaf', w1], winlayout(1)) + tabnew + let w2 = win_getid() + call assert_equal(['leaf', w2], 2->winlayout()) + tabclose endfunc diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 5bb6059fa7..5fec41f9a5 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -114,6 +114,12 @@ static void tinput_done_event(void **argv) static void tinput_wait_enqueue(void **argv) { TermInput *input = argv[0]; + if (rbuffer_size(input->key_buffer) == 0 && input->paste == 3) { + const String keys = { .data = "", .size = 0 }; + String copy = copy_string(keys); + multiqueue_put(main_loop.events, tinput_paste_event, 3, + copy.data, copy.size, (intptr_t)input->paste); + } RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { const String keys = { .data = buf, .size = len }; if (input->paste) { diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index bb75286369..e7a60aca49 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -308,23 +308,39 @@ static void terminfo_start(UI *ui) // Enable bracketed paste unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste); + int ret; uv_loop_init(&data->write_loop); if (data->out_isatty) { - uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0); + ret = uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0); + if (ret) { + ELOG("uv_tty_init failed: %s", uv_strerror(ret)); + } #ifdef WIN32 - uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW); + ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW); + if (ret) { + ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); + } #else int retry_count = 10; // A signal may cause uv_tty_set_mode() to fail (e.g., SIGCONT). Retry a // few times. #12322 - while (uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO) == UV_EINTR + while ((ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO)) == UV_EINTR && retry_count > 0) { retry_count--; } + if (ret) { + ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); + } #endif } else { - uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0); - uv_pipe_open(&data->output_handle.pipe, data->out_fd); + ret = uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0); + if (ret) { + ELOG("uv_pipe_init failed: %s", uv_strerror(ret)); + } + ret = uv_pipe_open(&data->output_handle.pipe, data->out_fd); + if (ret) { + ELOG("uv_pipe_open failed: %s", uv_strerror(ret)); + } } flush_buf(ui); } @@ -1086,8 +1102,14 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx) // after calling uv_tty_set_mode. So, set the mode of the TTY again here. // #13073 if (data->is_starting && data->input.in_fd == STDERR_FILENO) { - uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL); - uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO); + int ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL); + if (ret) { + ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); + } + ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO); + if (ret) { + ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); + } } #endif tui_set_mode(ui, (ModeShape)mode_idx); @@ -2081,8 +2103,11 @@ static void flush_buf(UI *ui) fwrite(bufs[i].base, bufs[i].len, 1, data->screenshot); } } else { - uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle), - bufs, (unsigned)(bufp - bufs), NULL); + int ret = uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle), + bufs, (unsigned)(bufp - bufs), NULL); + if (ret) { + ELOG("uv_write failed: %s", uv_strerror(ret)); + } uv_run(&data->write_loop, UV_RUN_DEFAULT); } data->bufpos = 0; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index aad72af025..1aadaf5c9d 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -23,7 +23,6 @@ #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" @@ -300,6 +299,44 @@ void ui_busy_stop(void) } } +/// Emit a bell or visualbell as a warning +/// +/// val is one of the BO_ values, e.g., BO_OPER +void vim_beep(unsigned val) +{ + called_vim_beep = true; + + if (emsg_silent == 0) { + if (!((bo_flags & val) || (bo_flags & BO_ALL))) { + static int beeps = 0; + static uint64_t start_time = 0; + + // Only beep up to three times per half a second, + // otherwise a sequence of beeps would freeze Vim. + if (start_time == 0 || os_hrtime() - start_time > 500000000u) { + beeps = 0; + start_time = os_hrtime(); + } + beeps++; + if (beeps <= 3) { + if (p_vb) { + ui_call_visual_bell(); + } else { + ui_call_bell(); + } + } + } + + // When 'debug' contains "beep" produce a message. If we are sourcing + // a script or executing a function give the user a hint where the beep + // comes from. + if (vim_strchr(p_debug, 'e') != NULL) { + msg_source(HL_ATTR(HLF_W)); + msg_attr(_("Beep!"), HL_ATTR(HLF_W)); + } + } +} + void ui_attach_impl(UI *ui, uint64_t chanid) { if (ui_count == MAX_UI_COUNT) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 7eb76abd2c..d18f35a43a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -93,13 +93,14 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/garray.h" +#include "nvim/getchar.h" #include "nvim/lib/kvec.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" +#include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/os_unix.h" @@ -922,7 +923,7 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi, const char *file_name) uhp->uh_time = undo_read_time(bi); // Unserialize optional fields. - for (;; ) { + for (;;) { int len = undo_read_byte(bi); if (len == EOF) { @@ -1504,7 +1505,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT // Optional header fields. long last_save_nr = 0; - for (;; ) { + for (;;) { int len = undo_read_byte(&bi); if (len == 0 || len == EOF) { @@ -2621,7 +2622,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet) if (uhp == NULL) { *msgbuf = NUL; } else { - add_time(msgbuf, sizeof(msgbuf), uhp->uh_time); + undo_fmt_time(msgbuf, sizeof(msgbuf), uhp->uh_time); } { @@ -2641,6 +2642,29 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet) msgbuf); } +/// Put the timestamp of an undo header in "buf[buflen]" in a nice format. +void undo_fmt_time(char_u *buf, size_t buflen, time_t tt) +{ + struct tm curtime; + + if (time(NULL) - tt >= 100) { + os_localtime_r(&tt, &curtime); + if (time(NULL) - tt < (60L * 60L * 12L)) { + // within 12 hours + (void)strftime((char *)buf, buflen, "%H:%M:%S", &curtime); + } else { + // longer ago + (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime); + } + } else { + int64_t seconds = time(NULL) - tt; + vim_snprintf((char *)buf, buflen, + NGETTEXT("%" PRId64 " second ago", + "%" PRId64 " seconds ago", (uint32_t)seconds), + seconds); + } +} + /// u_sync: stop adding to the current entry list /// /// @param force if true, also sync when no_u_sync is set. @@ -2683,16 +2707,13 @@ void ex_undolist(exarg_T *eap) while (uhp != NULL) { if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark && uhp->uh_walk != mark) { - vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", - uhp->uh_seq, changes); - add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), - uhp->uh_time); + vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes); + undo_fmt_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { while (STRLEN(IObuff) < 33) { STRCAT(IObuff, " "); } - vim_snprintf_add((char *)IObuff, IOSIZE, - " %3ld", uhp->uh_save_nr); + vim_snprintf_add((char *)IObuff, IOSIZE, " %3ld", uhp->uh_save_nr); } GA_APPEND(char_u *, &ga, vim_strsave(IObuff)); } diff --git a/src/nvim/version.c b/src/nvim/version.c index 1fcbae8be3..5e2a81795a 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -458,11 +458,11 @@ static const int included_patches[] = { 1466, 1465, 1464, - // 1463, + 1463, 1462, - // 1461, - // 1460, - // 1459, + 1461, + 1460, + 1459, 1458, 1457, 1456, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 726670f082..2f8ddd1e88 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -72,6 +72,8 @@ enum { NUMBUFLEN = 65, }; #define TERM_FOCUS 0x2000 // Terminal focus mode #define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview. +#define MODE_MAX_LENGTH 4 // max mode length returned in mode() + // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS) @@ -213,6 +215,11 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext() // (vim_strchr() is now in strings.c) #define STRLEN(s) strlen((char *)(s)) +#ifdef HAVE_STRNLEN +# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n)) +#else +# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n)) +#endif #define STRCPY(d, s) strcpy((char *)(d), (char *)(s)) #define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n)) #define STRLCPY(d, s, n) xstrlcpy((char *)(d), (char *)(s), (size_t)(n)) diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 4f028fa87f..ba6cfab98b 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -204,40 +204,40 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) } \ } while (0) switch (schar) { - // Paired brackets. + // Paired brackets. #define BRACKET(typ, opning, clsing) \ -case opning: \ -case clsing: { \ - ret.type = typ; \ - ret.data.brc.closing = (schar == clsing); \ - break; \ -} - BRACKET(kExprLexParenthesis, '(', ')') - BRACKET(kExprLexBracket, '[', ']') - BRACKET(kExprLexFigureBrace, '{', '}') + case opning: \ + case clsing: { \ + ret.type = typ; \ + ret.data.brc.closing = (schar == clsing); \ + break; \ + } + BRACKET(kExprLexParenthesis, '(', ')') + BRACKET(kExprLexBracket, '[', ']') + BRACKET(kExprLexFigureBrace, '{', '}') #undef BRACKET - // Single character tokens without data. + // Single character tokens without data. #define CHAR(typ, ch) \ -case ch: { \ - ret.type = typ; \ - break; \ -} - CHAR(kExprLexQuestion, '?') - CHAR(kExprLexColon, ':') - CHAR(kExprLexComma, ',') + case ch: { \ + ret.type = typ; \ + break; \ + } + CHAR(kExprLexQuestion, '?') + CHAR(kExprLexColon, ':') + CHAR(kExprLexComma, ',') #undef CHAR - // Multiplication/division/modulo. + // Multiplication/division/modulo. #define MUL(mul_type, ch) \ -case ch: { \ - ret.type = kExprLexMultiplication; \ - ret.data.mul.type = mul_type; \ - break; \ -} - MUL(kExprLexMulMul, '*') - MUL(kExprLexMulDiv, '/') - MUL(kExprLexMulMod, '%') + case ch: { \ + ret.type = kExprLexMultiplication; \ + ret.data.mul.type = mul_type; \ + break; \ + } + MUL(kExprLexMulMul, '*') + MUL(kExprLexMulDiv, '/') + MUL(kExprLexMulMod, '%') #undef MUL #define CHARREG(typ, cond) \ @@ -653,16 +653,16 @@ case ch: { \ // Sign or augmented assignment. #define CHAR_OR_ASSIGN(ch, ch_type, ass_type) \ -case ch: { \ - if (pline.size > 1 && pline.data[1] == '=') { \ - ret.len++; \ - ret.type = kExprLexAssignment; \ - ret.data.ass.type = ass_type; \ - } else { \ - ret.type = ch_type; \ - } \ - break; \ -} + case ch: { \ + if (pline.size > 1 && pline.data[1] == '=') { \ + ret.len++; \ + ret.type = kExprLexAssignment; \ + ret.data.ass.type = ass_type; \ + } else { \ + ret.type = ch_type; \ + } \ + break; \ + } CHAR_OR_ASSIGN('+', kExprLexPlus, kExprAsgnAdd) CHAR_OR_ASSIGN('.', kExprLexDot, kExprAsgnConcat) #undef CHAR_OR_ASSIGN @@ -811,19 +811,19 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, const LexExpr eltkn_type_tab[token.type]); switch (token.type) { #define TKNARGS(tkn_type, ...) \ -case tkn_type: { \ - ADDSTR(__VA_ARGS__); \ - break; \ -} - TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)", - eltkn_cmp_type_tab[token.data.cmp.type], - ccs_tab[token.data.cmp.ccs], - (int)token.data.cmp.inv) - TKNARGS(kExprLexMultiplication, "(type=%s)", - eltkn_mul_type_tab[token.data.mul.type]) - TKNARGS(kExprLexAssignment, "(type=%s)", - expr_asgn_type_tab[token.data.ass.type]) - TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name)) + case tkn_type: { \ + ADDSTR(__VA_ARGS__); \ + break; \ + } + TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)", + eltkn_cmp_type_tab[token.data.cmp.type], + ccs_tab[token.data.cmp.ccs], + (int)token.data.cmp.inv) + TKNARGS(kExprLexMultiplication, "(type=%s)", + eltkn_mul_type_tab[token.data.mul.type]) + TKNARGS(kExprLexAssignment, "(type=%s)", + expr_asgn_type_tab[token.data.ass.type]) + TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name)) case kExprLexDoubleQuotedString: TKNARGS(kExprLexSingleQuotedString, "(closed=%i)", (int)token.data.str.closed) @@ -1036,7 +1036,7 @@ void viml_pexpr_free_ast(ExprAST ast) ExprASTNode **const cur_node = kv_last(ast_stack); #ifndef NDEBUG // Explicitly check for AST recursiveness. - for (size_t i = 0 ; i < kv_size(ast_stack) - 1 ; i++) { + for (size_t i = 0; i < kv_size(ast_stack) - 1; i++) { assert(*kv_A(ast_stack, i) != *cur_node); } #endif @@ -1540,21 +1540,21 @@ static inline void east_set_error(const ParserState *const pstate, ExprASTError case kExprNodeComplexIdentifier: \ case kExprNodePlainIdentifier: \ case kExprNodeCurlyBracesIdentifier: { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \ - cur_node->len = 0; \ - cur_node->children = *top_node_p; \ - *top_node_p = cur_node; \ - kvi_push(ast_stack, &cur_node->children->next); \ - ExprASTNode **const new_top_node_p = kv_last(ast_stack); \ - assert(*new_top_node_p == NULL); \ - new_ident_node_code; \ - *new_top_node_p = cur_node; \ - HL_CUR_TOKEN(hl); \ - break; \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \ + cur_node->len = 0; \ + cur_node->children = *top_node_p; \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children->next); \ + ExprASTNode **const new_top_node_p = kv_last(ast_stack); \ + assert(*new_top_node_p == NULL); \ + new_ident_node_code; \ + *new_top_node_p = cur_node; \ + HL_CUR_TOKEN(hl); \ + break; \ } \ default: { \ - OP_MISSING; \ - break; \ + OP_MISSING; \ + break; \ } \ } \ } while (0) @@ -1747,19 +1747,19 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no const char *const v_p_start = v_p; switch (*p) { #define SINGLE_CHAR_ESC(ch, real_ch) \ -case ch: { \ - *v_p++ = real_ch; \ - p++; \ - break; \ -} - SINGLE_CHAR_ESC('b', BS) - SINGLE_CHAR_ESC('e', ESC) - SINGLE_CHAR_ESC('f', FF) - SINGLE_CHAR_ESC('n', NL) - SINGLE_CHAR_ESC('r', CAR) - SINGLE_CHAR_ESC('t', TAB) - SINGLE_CHAR_ESC('"', '"') - SINGLE_CHAR_ESC('\\', '\\') + case ch: { \ + *v_p++ = real_ch; \ + p++; \ + break; \ + } + SINGLE_CHAR_ESC('b', BS) + SINGLE_CHAR_ESC('e', ESC) + SINGLE_CHAR_ESC('f', FF) + SINGLE_CHAR_ESC('n', NL) + SINGLE_CHAR_ESC('r', CAR) + SINGLE_CHAR_ESC('t', TAB) + SINGLE_CHAR_ESC('"', '"') + SINGLE_CHAR_ESC('\\', '\\') #undef SINGLE_CHAR_ESC // Hexadecimal or unicode. @@ -2141,32 +2141,32 @@ viml_pexpr_parse_process_token: break; } #define SIMPLE_UB_OP(op) \ -case kExprLex##op: { \ - if (want_node == kENodeValue) { \ - /* Value level: assume unary operator. */ \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ - *top_node_p = cur_node; \ - kvi_push(ast_stack, &cur_node->children); \ - HL_CUR_TOKEN(Unary##op); \ - } else { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ - ADD_OP_NODE(cur_node); \ - HL_CUR_TOKEN(Binary##op); \ - } \ - want_node = kENodeValue; \ - break; \ -} + case kExprLex##op: { \ + if (want_node == kENodeValue) { \ + /* Value level: assume unary operator. */ \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children); \ + HL_CUR_TOKEN(Unary##op); \ + } else { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ + ADD_OP_NODE(cur_node); \ + HL_CUR_TOKEN(Binary##op); \ + } \ + want_node = kENodeValue; \ + break; \ + } SIMPLE_UB_OP(Plus) SIMPLE_UB_OP(Minus) #undef SIMPLE_UB_OP #define SIMPLE_B_OP(op, msg) \ -case kExprLex##op: { \ - ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ - HL_CUR_TOKEN(op); \ - ADD_OP_NODE(cur_node); \ - break; \ -} + case kExprLex##op: { \ + ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ + HL_CUR_TOKEN(op); \ + ADD_OP_NODE(cur_node); \ + break; \ + } SIMPLE_B_OP(Or, "or operator") SIMPLE_B_OP(And, "and operator") #undef SIMPLE_B_OP @@ -2174,14 +2174,14 @@ case kExprLex##op: { \ ADD_VALUE_IF_MISSING(_("E15: Unexpected multiplication-like operator: %.*s")); switch (cur_token.data.mul.type) { #define MUL_OP(lex_op_tail, node_op_tail) \ -case kExprLexMul##lex_op_tail: { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ - HL_CUR_TOKEN(node_op_tail); \ - break; \ -} - MUL_OP(Mul, Multiplication) - MUL_OP(Div, Division) - MUL_OP(Mod, Mod) + case kExprLexMul##lex_op_tail: { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ + HL_CUR_TOKEN(node_op_tail); \ + break; \ + } + MUL_OP(Mul, Multiplication) + MUL_OP(Div, Division) + MUL_OP(Mod, Mod) #undef MUL_OP } ADD_OP_NODE(cur_node); @@ -2929,11 +2929,11 @@ viml_pexpr_parse_no_paren_closing_error: {} cur_node->data.ass.type = cur_token.data.ass.type; switch (cur_token.data.ass.type) { #define HL_ASGN(asgn, hl) \ -case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } - HL_ASGN(Plain, PlainAssignment) - HL_ASGN(Add, AssignmentWithAddition) - HL_ASGN(Subtract, AssignmentWithSubtraction) - HL_ASGN(Concat, AssignmentWithConcatenation) + case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } + HL_ASGN(Plain, PlainAssignment) + HL_ASGN(Add, AssignmentWithAddition) + HL_ASGN(Subtract, AssignmentWithSubtraction) + HL_ASGN(Concat, AssignmentWithConcatenation) #undef HL_ASGN } ADD_OP_NODE(cur_node); diff --git a/src/nvim/window.c b/src/nvim/window.c index ff5b39eb84..c711f462d1 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -30,7 +30,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -224,7 +223,7 @@ newwindow: beep_flush(); } else { if (Prenum) { // go to specified window - for (wp = firstwin; --Prenum > 0; ) { + for (wp = firstwin; --Prenum > 0;) { if (wp->w_next == NULL) { break; } else { @@ -569,7 +568,7 @@ wingotofile: static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Prenum) { - size_t len = xstrlcpy((char *)bufp, cmd, bufsize); + size_t len = STRLCPY(bufp, cmd, bufsize); if (Prenum > 0 && len < bufsize) { vim_snprintf((char *)bufp + len, bufsize - len, "%" PRId64, Prenum); @@ -1958,8 +1957,8 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // Set the width/height of this frame. // Redraw when size or position changes if (topfr->fr_height != height || topfr->fr_win->w_winrow != row - || topfr->fr_width != width || - topfr->fr_win->w_wincol != col) { + || topfr->fr_width != width + || topfr->fr_win->w_wincol != col) { topfr->fr_win->w_winrow = row; frame_new_height(topfr, height, false, false); topfr->fr_win->w_wincol = col; @@ -2229,6 +2228,54 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } } +static void leaving_window(win_T *const win) + FUNC_ATTR_NONNULL_ALL +{ + // Only matters for a prompt window. + if (!bt_prompt(win->w_buffer)) { + return; + } + + // When leaving a prompt window stop Insert mode and perhaps restart + // it when entering that window again. + win->w_buffer->b_prompt_insert = restart_edit; + if (restart_edit != NUL && mode_displayed) { + clear_cmdline = true; // unshow mode later + } + restart_edit = NUL; + + // When leaving the window (or closing the window) was done from a + // callback we need to break out of the Insert mode loop and restart Insert + // mode when entering the window again. + if (State & INSERT) { + stop_insert_mode = true; + if (win->w_buffer->b_prompt_insert == NUL) { + win->w_buffer->b_prompt_insert = 'A'; + } + } +} + +void entering_window(win_T *const win) + FUNC_ATTR_NONNULL_ALL +{ + // Only matters for a prompt window. + if (!bt_prompt(win->w_buffer)) { + return; + } + + // When switching to a prompt buffer that was in Insert mode, don't stop + // Insert mode, it may have been set in leaving_window(). + if (win->w_buffer->b_prompt_insert != NUL) { + stop_insert_mode = false; + } + + // When entering the prompt window restart Insert mode if we were in Insert + // mode when we left it and not already in Insert mode. + if ((State & INSERT) == 0) { + restart_edit = win->w_buffer->b_prompt_insert; + } +} + /// Closes all windows for buffer `buf`. /// /// @param keep_curwin don't close `curwin` @@ -2239,7 +2286,7 @@ void close_windows(buf_T *buf, int keep_curwin) ++RedrawingDisabled; - for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW; ) { + for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW;) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { if (win_close(wp, false) == FAIL) { @@ -2367,6 +2414,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev shell_new_rows(); } } + entering_window(curwin); // Since goto_tabpage_tp above did not trigger *Enter autocommands, do // that now. @@ -2434,10 +2482,10 @@ int win_close(win_T *win, bool free_buf) } if (win == curwin) { - /* - * Guess which window is going to be the new current window. - * This may change because of the autocommands (sigh). - */ + leaving_window(curwin); + + // Guess which window is going to be the new current window. + // This may change because of the autocommands (sigh). if (!win->w_floating) { wp = frame2win(win_altframe(win, NULL)); } else { @@ -2582,7 +2630,7 @@ int win_close(win_T *win, bool free_buf) * If the cursor goes to the preview or the quickfix window, try * finding another window to go to. */ - for (;; ) { + for (;;) { if (wp->w_next == NULL) { wp = firstwin; } else { @@ -3147,12 +3195,12 @@ static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wf break; } if (topfirst) { - do{ + do { frp = frp->fr_next; } while (wfh && frp != NULL && frame_fixed_height(frp)); } else { - do{ + do { frp = frp->fr_prev; } while (wfh && frp != NULL && frame_fixed_height(frp)); @@ -3347,12 +3395,12 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw break; } if (leftfirst) { - do{ + do { frp = frp->fr_next; } while (wfw && frp != NULL && frame_fixed_width(frp)); } else { - do{ + do { frp = frp->fr_prev; } while (wfw && frp != NULL && frame_fixed_width(frp)); @@ -3801,6 +3849,8 @@ int win_new_tabpage(int after, char_u *filename) lastused_tabpage = old_curtab; + entering_window(curwin); + apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); @@ -3956,6 +4006,7 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds) { tabpage_T *tp = curtab; + leaving_window(curwin); reset_VIsual_and_resel(); // stop Visual mode if (trigger_leave_autocmds) { if (new_curbuf != curbuf) { @@ -4316,7 +4367,7 @@ win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) * downwards neighbor. */ fr = foundfr; - for (;; ) { + for (;;) { if (fr == tp->tp_topframe) { goto end; } @@ -4334,7 +4385,7 @@ win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) /* * Now go downwards to find the bottom or top frame in it. */ - for (;; ) { + for (;;) { if (nfr->fr_layout == FR_LEAF) { foundfr = nfr; break; @@ -4399,7 +4450,7 @@ win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) * right neighbor. */ fr = foundfr; - for (;; ) { + for (;;) { if (fr == tp->tp_topframe) { goto end; } @@ -4417,7 +4468,7 @@ win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) /* * Now go downwards to find the leftmost or rightmost frame in it. */ - for (;; ) { + for (;;) { if (nfr->fr_layout == FR_LEAF) { foundfr = nfr; break; @@ -4478,6 +4529,10 @@ static void win_enter_ext(win_T *const wp, const int flags) return; } + if (!curwin_invalid) { + leaving_window(curwin); + } + if (!curwin_invalid && (flags & WEE_TRIGGER_LEAVE_AUTOCMDS)) { // Be careful: If autocommands delete the window, return now. if (wp->w_buffer != curbuf) { @@ -4523,42 +4578,10 @@ static void win_enter_ext(win_T *const wp, const int flags) } changed_line_abv_curs(); // assume cursor position needs updating - // New directory is either the local directory of the window, tab or NULL. - char *new_dir = (char *)(curwin->w_localdir - ? curwin->w_localdir : curtab->tp_localdir); - - char cwd[MAXPATHL]; - if (os_dirname((char_u *)cwd, MAXPATHL) != OK) { - cwd[0] = NUL; - } - - if (new_dir) { - // Window/tab has a local directory: Save current directory as global - // (unless that was done already) and change to the local directory. - if (globaldir == NULL) { - if (cwd[0] != NUL) { - globaldir = (char_u *)xstrdup(cwd); - } - } - if (os_chdir(new_dir) == 0) { - if (!p_acd && pathcmp(new_dir, cwd, -1) != 0) { - do_autocmd_dirchanged(new_dir, curwin->w_localdir - ? kCdScopeWindow : kCdScopeTabpage, kCdCauseWindow); - } - shorten_fnames(true); - } - } else if (globaldir != NULL) { - // Window doesn't have a local directory and we are not in the global - // directory: Change to the global directory. - if (os_chdir((char *)globaldir) == 0) { - if (!p_acd && pathcmp((char *)globaldir, cwd, -1) != 0) { - do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow); - } - } - XFREE_CLEAR(globaldir); - shorten_fnames(true); - } + fix_current_dir(); + entering_window(curwin); + // Careful: autocommands may close the window and make "wp" invalid if (flags & WEE_TRIGGER_NEW_AUTOCMDS) { apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); } @@ -4592,7 +4615,7 @@ static void win_enter_ext(win_T *const wp, const int flags) } // set window width to desired minimal value - if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !wp->w_floating) { + if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !curwin->w_floating) { win_setwidth((int)p_wiw); } @@ -4602,6 +4625,46 @@ static void win_enter_ext(win_T *const wp, const int flags) do_autochdir(); } +/// Used after making another window the current one: change directory if needed. +void fix_current_dir(void) +{ + // New directory is either the local directory of the window, tab or NULL. + char *new_dir = (char *)(curwin->w_localdir + ? curwin->w_localdir : curtab->tp_localdir); + char cwd[MAXPATHL]; + if (os_dirname((char_u *)cwd, MAXPATHL) != OK) { + cwd[0] = NUL; + } + + if (new_dir) { + // Window/tab has a local directory: Save current directory as global + // (unless that was done already) and change to the local directory. + if (globaldir == NULL) { + if (cwd[0] != NUL) { + globaldir = (char_u *)xstrdup(cwd); + } + } + if (os_chdir(new_dir) == 0) { + if (!p_acd && pathcmp(new_dir, cwd, -1) != 0) { + do_autocmd_dirchanged(new_dir, curwin->w_localdir + ? kCdScopeWindow : kCdScopeTabpage, kCdCauseWindow); + } + last_chdir_reason = NULL; + shorten_fnames(true); + } + } else if (globaldir != NULL) { + // Window doesn't have a local directory and we are not in the global + // directory: Change to the global directory. + if (os_chdir((char *)globaldir) == 0) { + if (!p_acd && pathcmp((char *)globaldir, cwd, -1) != 0) { + do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow); + } + } + XFREE_CLEAR(globaldir); + last_chdir_reason = NULL; + shorten_fnames(true); + } +} /// Jump to the first open window that contains buffer "buf", if one exists. /// Returns a pointer to the window found, otherwise NULL. @@ -4750,6 +4813,8 @@ static void win_free(win_T *wp, tabpage_T *tp) clear_winopt(&wp->w_onebuf_opt); clear_winopt(&wp->w_allbuf_opt); + xfree(wp->w_p_lcs_chars.multispace); + vars_clear(&wp->w_vars->dv_hashtab); // free all w: variables hash_init(&wp->w_vars->dv_hashtab); unref_var_dict(wp->w_vars); @@ -5823,8 +5888,8 @@ void scroll_to_fraction(win_T *wp, int prev_height) // is visible. if (height > 0 && (!wp->w_p_scb || wp == curwin) - && (height < wp->w_buffer->b_ml.ml_line_count || - wp->w_topline > 1)) { + && (height < wp->w_buffer->b_ml.ml_line_count + || wp->w_topline > 1)) { /* * Find a value for w_topline that shows the cursor at the same * relative position in the window as before (more or less). @@ -6080,7 +6145,7 @@ void command_height(void) static void frame_add_height(frame_T *frp, int n) { frame_new_height(frp, frp->fr_height + n, false, false); - for (;; ) { + for (;;) { frp = frp->fr_parent; if (frp == NULL) { break; @@ -6624,6 +6689,9 @@ void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, bool no_dis curwin = save_curwin; curbuf = curwin->w_buffer; } + // If called by win_execute() and executing the command changed the + // directory, it now has to be restored. + fix_current_dir(); } /// Make "buf" the current buffer. @@ -7003,7 +7071,7 @@ int win_getid(typval_T *argvars) wp = tp->tp_firstwin; } } - for ( ; wp != NULL; wp = wp->w_next) { + for (; wp != NULL; wp = wp->w_next) { if (--winnr == 0) { return wp->handle; } diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg index c06bf41d47..49ce394dc9 100644 --- a/src/uncrustify.cfg +++ b/src/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.73.0-199-0dfafb27 +# Uncrustify-0.74.0 # # General options @@ -141,7 +141,7 @@ sp_enum_before_assign = ignore # ignore/add/remove/force/not_defined # Add or remove space after assignment '=' in enum. # # Overrides sp_enum_assign. -sp_enum_after_assign = ignore # ignore/add/remove/force/not_defined +sp_enum_after_assign = force # ignore/add/remove/force/not_defined # Add or remove space around assignment ':' in enum. sp_enum_colon = ignore # ignore/add/remove/force/not_defined @@ -149,11 +149,11 @@ sp_enum_colon = ignore # ignore/add/remove/force/not_defined # Add or remove space around preprocessor '##' concatenation operator. # # Default: add -sp_pp_concat = ignore # ignore/add/remove/force/not_defined +sp_pp_concat = remove # ignore/add/remove/force/not_defined # Add or remove space after preprocessor '#' stringify operator. # Also affects the '#@' charizing operator. -sp_pp_stringify = ignore # ignore/add/remove/force/not_defined +sp_pp_stringify = remove # ignore/add/remove/force/not_defined # Add or remove space before preprocessor '#' stringify operator # as in '#define x(y) L#y'. @@ -214,6 +214,10 @@ sp_after_ptr_star_func = remove # ignore/add/remove/force/not_defined # function prototype or function definition. sp_after_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined +# Add or remove space between the pointer star '*' and the name of the variable +# in a function pointer definition. +sp_ptr_star_func_var = ignore # ignore/add/remove/force/not_defined + # Add or remove space after a pointer star '*', if followed by an open # parenthesis, as in 'void* (*)()'. sp_ptr_star_paren = ignore # ignore/add/remove/force/not_defined @@ -311,19 +315,33 @@ sp_permit_cpp11_shift = false # true/false # 'while', etc.). sp_before_sparen = force # ignore/add/remove/force/not_defined -# Add or remove space inside '(' and ')' of control statements. +# Add or remove space inside '(' and ')' of control statements other than +# 'for'. sp_inside_sparen = remove # ignore/add/remove/force/not_defined -# Add or remove space after '(' of control statements. +# Add or remove space after '(' of control statements other than 'for'. # # Overrides sp_inside_sparen. sp_inside_sparen_open = remove # ignore/add/remove/force/not_defined -# Add or remove space before ')' of control statements. +# Add or remove space before ')' of control statements other than 'for'. # # Overrides sp_inside_sparen. sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined +# Add or remove space inside '(' and ')' of 'for' statements. +sp_inside_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after '(' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ')' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_close = ignore # ignore/add/remove/force/not_defined + # Add or remove space between '((' or '))' of control statements. sp_sparen_paren = ignore # ignore/add/remove/force/not_defined @@ -334,10 +352,10 @@ sp_after_sparen = ignore # ignore/add/remove/force/not_defined sp_sparen_brace = ignore # ignore/add/remove/force/not_defined # Add or remove space between 'do' and '{'. -sp_do_brace_open = ignore # ignore/add/remove/force/not_defined +sp_do_brace_open = force # ignore/add/remove/force/not_defined # Add or remove space between '}' and 'while'. -sp_brace_close_while = ignore # ignore/add/remove/force/not_defined +sp_brace_close_while = force # ignore/add/remove/force/not_defined # Add or remove space between 'while' and '('. Overrides sp_before_sparen. sp_while_paren_open = ignore # ignore/add/remove/force/not_defined @@ -354,18 +372,18 @@ sp_special_semi = ignore # ignore/add/remove/force/not_defined # Add or remove space before ';'. # # Default: remove -sp_before_semi = ignore # ignore/add/remove/force/not_defined +sp_before_semi = remove # ignore/add/remove/force/not_defined # Add or remove space before ';' in non-empty 'for' statements. -sp_before_semi_for = ignore # ignore/add/remove/force/not_defined +sp_before_semi_for = remove # ignore/add/remove/force/not_defined # Add or remove space before a semicolon of an empty left part of a for # statement, as in 'for ( <here> ; ; )'. -sp_before_semi_for_empty = ignore # ignore/add/remove/force/not_defined +sp_before_semi_for_empty = remove # ignore/add/remove/force/not_defined # Add or remove space between the semicolons of an empty middle part of a for # statement, as in 'for ( ; <here> ; )'. -sp_between_semi_for_empty = ignore # ignore/add/remove/force/not_defined +sp_between_semi_for_empty = remove # ignore/add/remove/force/not_defined # Add or remove space after ';', except when followed by a comment. # @@ -379,10 +397,10 @@ sp_after_semi_for = force # ignore/add/remove/force/not_defined # Add or remove space after the final semicolon of an empty part of a for # statement, as in 'for ( ; ; <here> )'. -sp_after_semi_for_empty = ignore # ignore/add/remove/force/not_defined +sp_after_semi_for_empty = remove # ignore/add/remove/force/not_defined # Add or remove space before '[' (except '[]'). -sp_before_square = ignore # ignore/add/remove/force/not_defined +sp_before_square = remove # ignore/add/remove/force/not_defined # Add or remove space before '[' for a variable definition. # @@ -393,13 +411,13 @@ sp_before_vardef_square = remove # ignore/add/remove/force/not_defined sp_before_square_asm_block = ignore # ignore/add/remove/force/not_defined # Add or remove space before '[]'. -sp_before_squares = ignore # ignore/add/remove/force/not_defined +sp_before_squares = remove # ignore/add/remove/force/not_defined # Add or remove space before C++17 structured bindings. sp_cpp_before_struct_binding = ignore # ignore/add/remove/force/not_defined # Add or remove space inside a non-empty '[' and ']'. -sp_inside_square = ignore # ignore/add/remove/force/not_defined +sp_inside_square = remove # ignore/add/remove/force/not_defined # Add or remove space inside '[]'. sp_inside_square_empty = ignore # ignore/add/remove/force/not_defined @@ -592,18 +610,18 @@ sp_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined # Add or remove space inside empty function '()'. # Overrides sp_after_angle unless use_sp_after_angle_always is set to true. -sp_inside_fparens = ignore # ignore/add/remove/force/not_defined +sp_inside_fparens = remove # ignore/add/remove/force/not_defined # Add or remove space inside function '(' and ')'. -sp_inside_fparen = ignore # ignore/add/remove/force/not_defined +sp_inside_fparen = remove # ignore/add/remove/force/not_defined # Add or remove space inside the first parentheses in a function type, as in # 'void (*x)(...)'. -sp_inside_tparen = ignore # ignore/add/remove/force/not_defined +sp_inside_tparen = remove # ignore/add/remove/force/not_defined # Add or remove space between the ')' and '(' in a function type, as in # 'void (*x)(...)'. -sp_after_tparen_close = ignore # ignore/add/remove/force/not_defined +sp_after_tparen_close = remove # ignore/add/remove/force/not_defined # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = ignore # ignore/add/remove/force/not_defined @@ -648,8 +666,13 @@ sp_func_class_paren = ignore # ignore/add/remove/force/not_defined # and '()'. sp_func_class_paren_empty = ignore # ignore/add/remove/force/not_defined +# Add or remove space after 'return'. +# +# Default: force +sp_return = force # ignore/add/remove/force/not_defined + # Add or remove space between 'return' and '('. -sp_return_paren = ignore # ignore/add/remove/force/not_defined +sp_return_paren = force # ignore/add/remove/force/not_defined # Add or remove space between 'return' and '{'. sp_return_brace = ignore # ignore/add/remove/force/not_defined @@ -714,7 +737,7 @@ sp_else_brace = add # ignore/add/remove/force/not_defined sp_brace_else = add # ignore/add/remove/force/not_defined # Add or remove space between '}' and the name of a typedef on the same line. -sp_brace_typedef = ignore # ignore/add/remove/force/not_defined +sp_brace_typedef = force # ignore/add/remove/force/not_defined # Add or remove space before the '{' of a 'catch' statement, if the '{' and # 'catch' are on the same line, as in 'catch (decl) <here> {'. @@ -971,11 +994,31 @@ sp_inside_newop_paren_open = ignore # ignore/add/remove/force/not_defined # Overrides sp_inside_newop_paren. sp_inside_newop_paren_close = ignore # ignore/add/remove/force/not_defined -# Add or remove space before a trailing or embedded comment. -sp_before_tr_emb_cmt = add # ignore/add/remove/force/not_defined +# Add or remove space before a trailing comment. +sp_before_tr_cmt = add # ignore/add/remove/force/not_defined + +# Number of spaces before a trailing comment. +sp_num_before_tr_cmt = 2 # unsigned number + +# Add or remove space before an embedded comment. +# +# Default: force +sp_before_emb_cmt = force # ignore/add/remove/force/not_defined + +# Number of spaces before an embedded comment. +# +# Default: 1 +sp_num_before_emb_cmt = 1 # unsigned number + +# Add or remove space after an embedded comment. +# +# Default: force +sp_after_emb_cmt = force # ignore/add/remove/force/not_defined -# Number of spaces before a trailing or embedded comment. -sp_num_before_tr_emb_cmt = 2 # unsigned number +# Number of spaces after an embedded comment. +# +# Default: 1 +sp_num_after_emb_cmt = 1 # unsigned number # (Java) Add or remove space between an annotation and the open parenthesis. sp_annotation_paren = ignore # ignore/add/remove/force/not_defined @@ -1216,12 +1259,16 @@ indent_sparen_extra = 0 # number indent_relative_single_line_comments = true # true/false # Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. -# It might wise to choose the same value for the option indent_case_brace. +# It might be wise to choose the same value for the option indent_case_brace. indent_switch_case = 0 # unsigned number +# Spaces to indent the body of a 'switch' before any 'case'. +# Usually the same as indent_columns or indent_switch_case. +indent_switch_body = 0 # unsigned number + # Spaces to indent '{' from 'case'. By default, the brace will appear under # the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. -# It might wise to choose the same value for the option indent_switch_case. +# It might be wise to choose the same value for the option indent_switch_case. indent_case_brace = 0 # number # indent 'break' with 'case' from 'switch'. @@ -1236,13 +1283,31 @@ indent_switch_pp = true # true/false # Usually 0. indent_case_shift = 0 # unsigned number +# Whether to align comments before 'case' with the 'case'. +# +# Default: true +indent_case_comment = true # true/false + +# Whether to indent comments not found in first column. +# +# Default: true +indent_comment = true # true/false + # Whether to indent comments found in first column. indent_col1_comment = false # true/false # Whether to indent multi string literal in first column. indent_col1_multi_string_literal = false # true/false -# How to indent goto labels. +# Align comments on adjacent lines that are this many columns apart or less. +# +# Default: 3 +indent_comment_align_thresh = 3 # unsigned number + +# Whether to ignore indent for goto labels. +indent_ignore_label = false # true/false + +# How to indent goto labels. Requires indent_ignore_label=false. # # >0: Absolute column where 1 is the leftmost column # <=0: Subtract from brace indent @@ -1414,7 +1479,7 @@ indent_using_block = true # true/false # 0: Off (default) # 1: When the `if_false` is a continuation, indent it under `if_false` # 2: When the `:` is a continuation, indent it under `?` -indent_ternary_operator = 2 # unsigned number +indent_ternary_operator = 0 # unsigned number # Whether to indent the statements inside ternary operator. indent_inside_ternary_operator = false # true/false @@ -1632,7 +1697,7 @@ nl_using_brace = ignore # ignore/add/remove/force/not_defined nl_brace_brace = ignore # ignore/add/remove/force/not_defined # Add or remove newline between 'do' and '{'. -nl_do_brace = ignore # ignore/add/remove/force/not_defined +nl_do_brace = remove # ignore/add/remove/force/not_defined # Add or remove newline between '}' and 'while' of 'do' statement. nl_brace_while = ignore # ignore/add/remove/force/not_defined @@ -2333,7 +2398,7 @@ pos_arith = ignore # ignore/break/force/lead/trail/join/ pos_assign = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of Boolean operators in wrapped expressions. -pos_bool = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force +pos_bool = lead # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of comparison operators in wrapped expressions. pos_compare = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force @@ -2622,6 +2687,22 @@ align_right_cmt_at_col = 0 # unsigned number # 0: Don't align (default). align_func_proto_span = 0 # unsigned number +# How to consider (or treat) the '*' in the alignment of function prototypes. +# +# 0: Part of the type 'void * foo();' (default) +# 1: Part of the function 'void *foo();' +# 2: Dangling 'void *foo();' +# Dangling: the '*' will not be taken into account when aligning. +align_func_proto_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of function prototypes. +# +# 0: Part of the type 'long & foo();' (default) +# 1: Part of the function 'long &foo();' +# 2: Dangling 'long &foo();' +# Dangling: the '&' will not be taken into account when aligning. +align_func_proto_amp_style = 0 # unsigned number + # The threshold for aligning function prototypes. # Use a negative number for absolute thresholds. # @@ -3101,6 +3182,9 @@ pp_indent_in_guard = false # true/false # indented from column 1. pp_define_at_level = false # true/false +# Whether to indent '#include' at the brace level. +pp_include_at_level = false # true/false + # Whether to ignore the '#define' body while formatting. pp_ignore_define_body = false # true/false @@ -3307,5 +3391,5 @@ set QUESTION REAL_FATTR_CONST set QUESTION REAL_FATTR_NONNULL_ALL set QUESTION REAL_FATTR_PURE set QUESTION REAL_FATTR_WARN_UNUSED_RESULT -# option(s) with 'not default' value: 69 +# option(s) with 'not default' value: 86 # diff --git a/test/README.md b/test/README.md index 37aa54c157..c6e173ead2 100644 --- a/test/README.md +++ b/test/README.md @@ -116,7 +116,7 @@ Filtering Tests ### Filter by name -Another filter method is by setting a pattern of test name to `TEST_FILTER`. +Another filter method is by setting a pattern of test name to `TEST_FILTER` or `TEST_FILTER_OUT`. ``` lua it('foo api',function() @@ -131,6 +131,10 @@ To run only test with filter name: TEST_FILTER='foo.*api' make functionaltest +To run all tests except ones matching a filter: + + TEST_FILTER_OUT='foo.*api' make functionaltest + ### Filter by file To run a *specific* unit test: diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index a0c97804b7..688f3abba5 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -629,6 +629,13 @@ describe('api/buf', function() -- Doesn't change the global value eq([[^\s*#\s*define]], nvim('get_option', 'define')) end) + + it('returns values for unset local options', function() + -- 'undolevels' is only set to its "unset" value when a new buffer is + -- created + command('enew') + eq(-123456, curbuf('get_option', 'undolevels')) + end) end) describe('nvim_buf_get_name, nvim_buf_set_name', function() diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 50b4b85d2a..a8f538b951 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -104,10 +104,10 @@ describe('API/extmarks', function() it("can end extranges past final newline using end_col = 0", function() set_extmark(ns, marks[1], 0, 0, { end_col = 0, - end_line = 1 + end_row = 1 }) eq("end_col value outside range", - pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_line = 1 })) + pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 })) end) it('adds, updates and deletes marks', function() @@ -420,7 +420,7 @@ describe('API/extmarks', function() end) it('marks move with open line', function() - -- open_line in misc1.c + -- open_line in change.c -- testing marks below are also moved feed("yyP") set_extmark(ns, marks[1], 0, 4) @@ -489,7 +489,7 @@ describe('API/extmarks', function() end) it('marks move with line splits (using enter)', function() - -- open_line in misc1.c + -- open_line in change.c -- testing marks below are also moved feed("yyP") set_extmark(ns, marks[1], 0, 4) @@ -500,7 +500,7 @@ describe('API/extmarks', function() end) it('marks at last line move on insert new line', function() - -- open_line in misc1.c + -- open_line in change.c set_extmark(ns, marks[1], 0, 4) feed('0i<cr><esc>') check_undo_redo(ns, marks[1], 0, 4, 1, 4) @@ -1424,6 +1424,14 @@ describe('API/extmarks', function() eq({ {1, 0, 0}, {2, 0, 8} }, meths.buf_get_extmarks(0, ns, 0, -1, {})) end) + + it('can accept "end_row" or "end_line" #16548', function() + set_extmark(ns, marks[1], 0, 0, { + end_col = 0, + end_line = 1 + }) + eq({ {1, 0, 0, { end_col = 0, end_row = 1 }} }, get_extmarks(ns, 0, -1, {details=true})) + end) end) describe('Extmarks buffer api with many marks', function() diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 6367cc5caa..1c00f001ff 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -81,6 +81,8 @@ describe('notify', function() if isCI() then pending('hangs on CI #14083 #15251') return + elseif helpers.skip_fragile(pending) then + return end if helpers.pending_win32(pending) then return end local catchan = eval("jobstart(['cat'], {'rpc': v:true})") diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 21de4925b5..d53208a915 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -949,6 +949,33 @@ describe('API', function() end) end) + describe('nvim_get_option_value, nvim_set_option_value', function() + it('works', function() + ok(nvim('get_option_value', 'equalalways', {})) + nvim('set_option_value', 'equalalways', false, {}) + ok(not nvim('get_option_value', 'equalalways', {})) + end) + + it('can get local values when global value is set', function() + eq(0, nvim('get_option_value', 'scrolloff', {})) + eq(-1, nvim('get_option_value', 'scrolloff', {scope = 'local'})) + end) + + it('can set global and local values', function() + nvim('set_option_value', 'makeprg', 'hello', {}) + eq('hello', nvim('get_option_value', 'makeprg', {})) + eq('', nvim('get_option_value', 'makeprg', {scope = 'local'})) + nvim('set_option_value', 'makeprg', 'world', {scope = 'local'}) + eq('world', nvim('get_option_value', 'makeprg', {scope = 'local'})) + nvim('set_option_value', 'makeprg', 'goodbye', {scope = 'global'}) + eq('goodbye', nvim('get_option_value', 'makeprg', {scope = 'global'})) + nvim('set_option_value', 'makeprg', 'hello', {}) + eq('hello', nvim('get_option_value', 'makeprg', {scope = 'global'})) + eq('hello', nvim('get_option_value', 'makeprg', {})) + eq('', nvim('get_option_value', 'makeprg', {scope = 'local'})) + end) + end) + describe('nvim_{get,set}_current_buf, nvim_list_bufs', function() it('works', function() eq(1, #nvim('list_bufs')) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 11755a9d97..4d2ffa316a 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -222,9 +222,9 @@ describe('API/win', function() eq('', nvim('get_option', 'statusline')) command("set modified") command("enew") -- global-local: not preserved in new buffer - eq("Failed to get value for option 'statusline'", - pcall_err(curwin, 'get_option', 'statusline')) - eq('', eval('&l:statusline')) -- confirm local value was not copied + -- confirm local value was not copied + eq('', curwin('get_option', 'statusline')) + eq('', eval('&l:statusline')) end) it('after switching windows #15390', function() @@ -238,6 +238,10 @@ describe('API/win', function() eq('window-status', window('get_option', win1, 'statusline')) assert_alive() end) + + it('returns values for unset local options', function() + eq(-1, curwin('get_option', 'scrolloff')) + end) end) describe('get_position', function() diff --git a/test/functional/autocmd/modechanged_spec.lua b/test/functional/autocmd/modechanged_spec.lua new file mode 100644 index 0000000000..be5a291ac9 --- /dev/null +++ b/test/functional/autocmd/modechanged_spec.lua @@ -0,0 +1,31 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local feed, command = helpers.feed, helpers.command + +describe('ModeChanged', function() + before_each(function() + clear() + command('let g:count = 0') + command('au ModeChanged * let g:event = copy(v:event)') + command('au ModeChanged * let g:count += 1') + end) + + it('picks up terminal mode changes', function() + command("term") + feed('i') + eq({ + old_mode = 'nt', + new_mode = 't' + }, eval('g:event')) + feed('<c-\\><c-n>') + eq({ + old_mode = 't', + new_mode = 'nt' + }, eval('g:event')) + eq(3, eval('g:count')) + command("bd!") + + -- v:event is cleared after the autocommand is done + eq({}, eval('v:event')) + end) +end) diff --git a/test/functional/autocmd/recording_spec.lua b/test/functional/autocmd/recording_spec.lua new file mode 100644 index 0000000000..81152758de --- /dev/null +++ b/test/functional/autocmd/recording_spec.lua @@ -0,0 +1,52 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local source_vim = helpers.source + +describe('RecordingEnter', function() + before_each(clear) + it('works', function() + source_vim [[ + let g:recorded = 0 + autocmd RecordingEnter * let g:recorded += 1 + execute "normal! qqyyq" + ]] + eq(1, eval('g:recorded')) + end) + + it('gives a correct reg_recording()', function() + source_vim [[ + let g:recording = '' + autocmd RecordingEnter * let g:recording = reg_recording() + execute "normal! qqyyq" + ]] + eq('q', eval('g:recording')) + end) +end) + +describe('RecordingLeave', function() + before_each(clear) + it('works', function() + source_vim [[ + let g:recorded = 0 + autocmd RecordingLeave * let g:recorded += 1 + execute "normal! qqyyq" + ]] + eq(1, eval('g:recorded')) + end) + + it('gives the correct reg_recorded()', function() + source_vim [[ + let g:recorded = 'a' + let g:recording = '' + autocmd RecordingLeave * let g:recording = reg_recording() + autocmd RecordingLeave * let g:recorded = reg_recorded() + execute "normal! qqyyq" + ]] + eq('q', eval 'g:recording') + eq('', eval 'g:recorded') + eq('q', eval 'reg_recorded()') + end) +end) diff --git a/test/functional/autocmd/searchwrapped_spec.lua b/test/functional/autocmd/searchwrapped_spec.lua new file mode 100644 index 0000000000..46c2c99b3d --- /dev/null +++ b/test/functional/autocmd/searchwrapped_spec.lua @@ -0,0 +1,53 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local curbufmeths = helpers.curbufmeths +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed + +describe('autocmd SearchWrapped', function() + before_each(function() + clear() + command('set ignorecase') + command('let g:test = 0') + command('autocmd! SearchWrapped * let g:test += 1') + curbufmeths.set_lines(0, 1, false, { + 'The quick brown fox', + 'jumps over the lazy dog'}) + end) + + it('gets triggered when search wraps the end', function() + feed('/the<Return>') + eq(0, eval('g:test')) + + feed('n') + eq(1, eval('g:test')) + + feed('nn') + eq(2, eval('g:test')) + end) + + it('gets triggered when search wraps in reverse order', function() + feed('/the<Return>') + eq(0, eval('g:test')) + + feed('NN') + eq(1, eval('g:test')) + + feed('NN') + eq(2, eval('g:test')) + end) + + it('does not get triggered on failed searches', function() + feed('/blargh<Return>') + eq(0, eval('g:test')) + + feed('NN') + eq(0, eval('g:test')) + + feed('NN') + eq(0, eval('g:test')) + end) +end) diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua index f4c476560d..c68bc18eed 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -15,6 +15,7 @@ local read_file = helpers.read_file local trim = helpers.trim local currentdir = helpers.funcs.getcwd local iswin = helpers.iswin +local assert_alive = helpers.assert_alive describe('fileio', function() before_each(function() @@ -26,6 +27,7 @@ describe('fileio', function() os.remove('Xtest_startup_file1~') os.remove('Xtest_startup_file2') os.remove('Xtest_тест.md') + os.remove('Xtest-u8-int-max') rmdir('Xtest_startup_swapdir') rmdir('Xtest_backupdir') end) @@ -128,5 +130,12 @@ describe('fileio', function() table.insert(text, '') eq(text, funcs.readfile(fname, 'b')) end) + it('read invalid u8 over INT_MAX doesn\'t segfault', function() + clear() + command('call writefile(0zFFFFFFFF, "Xtest-u8-int-max")') + -- This should not segfault + command('edit ++enc=utf32 Xtest-u8-int-max') + assert_alive() + end) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index d1dce0f8da..4220d68ee1 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -560,7 +560,7 @@ describe('user config init', function() it('loads default lua config, but shows an error', function() clear{ args_rm={'-u'}, env=xenv } - feed('<cr>') -- TODO check this, test execution is blocked without it + feed('<cr>') -- confirm "Conflicting config ..." message eq(1, eval('g:lua_rc')) matches('^E5422: Conflicting configs', meths.exec('messages', true)) end) diff --git a/test/functional/editor/macro_spec.lua b/test/functional/editor/macro_spec.lua index 102d8fc723..c0c9256af2 100644 --- a/test/functional/editor/macro_spec.lua +++ b/test/functional/editor/macro_spec.lua @@ -6,6 +6,8 @@ local feed = helpers.feed local clear = helpers.clear local expect = helpers.expect local command = helpers.command +local insert = helpers.insert +local curbufmeths = helpers.curbufmeths describe('macros', function() before_each(clear) @@ -27,4 +29,29 @@ describe('macros', function() expect('llllll') eq(eval('@i'), 'lxxx') end) + + it('can be replayed with Q', function() + insert [[hello +hello +hello]] + feed [[gg]] + + feed [[qqAFOO<esc>q]] + eq({'helloFOO', 'hello', 'hello'}, curbufmeths.get_lines(0, -1, false)) + + feed[[Q]] + eq({'helloFOOFOO', 'hello', 'hello'}, curbufmeths.get_lines(0, -1, false)) + + feed[[G3Q]] + eq({'helloFOOFOO', 'hello', 'helloFOOFOOFOO'}, curbufmeths.get_lines(0, -1, false)) + end) +end) + +describe('reg_recorded()', function() + before_each(clear) + + it('returns the correct value', function() + feed [[qqyyq]] + eq('q', eval('reg_recorded()')) + end) end) diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index 283fcf9672..f9cce0deb6 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -294,7 +294,16 @@ describe("getcwd()", function () command('set autochdir') command('edit ' .. directories.global .. '/foo') eq(curdir .. pathsep .. directories.global, cwd()) + eq(curdir, wcwd()) + call('mkdir', 'bar') + command('edit ' .. 'bar/foo') + eq(curdir .. pathsep .. directories.global .. pathsep .. 'bar', cwd()) + eq(curdir, wcwd()) + command('lcd ..') + eq(curdir .. pathsep .. directories.global, cwd()) + eq(curdir .. pathsep .. directories.global, wcwd()) + command('edit') + eq(curdir .. pathsep .. directories.global .. pathsep .. 'bar', cwd()) + eq(curdir .. pathsep .. directories.global, wcwd()) end) end) - - diff --git a/test/functional/fixtures/api_level_8.mpack b/test/functional/fixtures/api_level_8.mpack Binary files differnew file mode 100644 index 0000000000..0447fce3ed --- /dev/null +++ b/test/functional/fixtures/api_level_8.mpack diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua deleted file mode 100644 index 8630ac58ef..0000000000 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ /dev/null @@ -1,1010 +0,0 @@ --- Tests for spell checking with 'encoding' set to "utf-8". - -local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -local write_file, call = helpers.write_file, helpers.call - -local function write_latin1(name, text) - text = call('iconv', text, 'utf-8', 'latin-1') - write_file(name, text) -end - -describe("spell checking with 'encoding' set to utf-8", function() - setup(function() - clear() - feed_command("syntax off") - write_latin1('Xtest1.aff',[[ - SET ISO8859-1 - TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - SOFOFROM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ¿ - SOFOTO ebctefghejklnnepkrstevvkesebctefghejklnnepkrstevvkeseeeeeeeceeeeeeeedneeeeeeeeeeepseeeeeeeeceeeeeeeedneeeeeeeeeeep? - - MIDWORD '- - - KEP = - RAR ? - BAD ! - - PFX I N 1 - PFX I 0 in . - - PFX O Y 1 - PFX O 0 out . - - SFX S Y 2 - SFX S 0 s [^s] - SFX S 0 es s - - SFX N N 3 - SFX N 0 en [^n] - SFX N 0 nen n - SFX N 0 n . - - REP 3 - REP g ch - REP ch g - REP svp s.v.p. - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - ]]) - write_latin1('Xtest1.dic', [[ - 123456 - test/NO - # comment - wrong - Comment - OK - uk - put/ISO - the end - deol - déôr - ]]) - write_latin1('Xtest2.aff', [[ - SET ISO8859-1 - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - PFXPOSTPONE - - MIDWORD '- - - KEP = - RAR ? - BAD ! - - PFX I N 1 - PFX I 0 in . - - PFX O Y 1 - PFX O 0 out [a-z] - - SFX S Y 2 - SFX S 0 s [^s] - SFX S 0 es s - - SFX N N 3 - SFX N 0 en [^n] - SFX N 0 nen n - SFX N 0 n . - - REP 3 - REP g ch - REP ch g - REP svp s.v.p. - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - ]]) - write_latin1('Xtest3.aff', [[ - SET ISO8859-1 - - COMPOUNDMIN 3 - COMPOUNDRULE m* - NEEDCOMPOUND x - ]]) - write_latin1('Xtest3.dic', [[ - 1234 - foo/m - bar/mx - mï/m - la/mx - ]]) - write_latin1('Xtest4.aff', [[ - SET ISO8859-1 - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - COMPOUNDRULE m+ - COMPOUNDRULE sm*e - COMPOUNDRULE sm+ - COMPOUNDMIN 3 - COMPOUNDWORDMAX 3 - COMPOUNDFORBIDFLAG t - - COMPOUNDSYLMAX 5 - SYLLABLE aáeéiíoóöõuúüûy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - - NEEDAFFIX x - - PFXPOSTPONE - - MIDWORD '- - - SFX q N 1 - SFX q 0 -ok . - - SFX a Y 2 - SFX a 0 s . - SFX a 0 ize/t . - - PFX p N 1 - PFX p 0 pre . - - PFX P N 1 - PFX P 0 nou . - ]]) - write_latin1('Xtest4.dic', [[ - 1234 - word/mP - util/am - pro/xq - tomato/m - bork/mp - start/s - end/e - ]]) - write_latin1('Xtest5.aff', [[ - SET ISO8859-1 - - FLAG long - - NEEDAFFIX !! - - COMPOUNDRULE ssmm*ee - - NEEDCOMPOUND xx - COMPOUNDPERMITFLAG pp - - SFX 13 Y 1 - SFX 13 0 bork . - - SFX a1 Y 1 - SFX a1 0 a1 . - - SFX aé Y 1 - SFX aé 0 aé . - - PFX zz Y 1 - PFX zz 0 pre/pp . - - PFX yy Y 1 - PFX yy 0 nou . - ]]) - write_latin1('Xtest5.dic', [[ - 1234 - foo/a1aé!! - bar/zz13ee - start/ss - end/eeyy - middle/mmxx - ]]) - write_latin1('Xtest6.aff', [[ - SET ISO8859-1 - - FLAG caplong - - NEEDAFFIX A! - - COMPOUNDRULE sMm*Ee - - NEEDCOMPOUND Xx - - COMPOUNDPERMITFLAG p - - SFX N3 Y 1 - SFX N3 0 bork . - - SFX A1 Y 1 - SFX A1 0 a1 . - - SFX Aé Y 1 - SFX Aé 0 aé . - - PFX Zz Y 1 - PFX Zz 0 pre/p . - ]]) - write_latin1('Xtest6.dic', [[ - 1234 - mee/A1AéA! - bar/ZzN3Ee - lead/s - end/Ee - middle/MmXx - ]]) - write_latin1('Xtest7.aff', [[ - SET ISO8859-1 - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - FLAG num - - NEEDAFFIX 9999 - - COMPOUNDRULE 2,77*123 - - NEEDCOMPOUND 1 - COMPOUNDPERMITFLAG 432 - - SFX 61003 Y 1 - SFX 61003 0 meat . - - SFX 391 Y 1 - SFX 391 0 a1 . - - SFX 111 Y 1 - SFX 111 0 aé . - - PFX 17 Y 1 - PFX 17 0 pre/432 . - ]]) - write_latin1('Xtest7.dic', [[ - 1234 - mee/391,111,9999 - bar/17,61003,123 - lead/2 - tail/123 - middle/77,1 - ]]) - write_latin1('Xtest8.aff', [[ - SET ISO8859-1 - - NOSPLITSUGS - ]]) - write_latin1('Xtest8.dic', [[ - 1234 - foo - bar - faabar - ]]) - write_latin1('Xtest9.aff', [[ - ]]) - write_latin1('Xtest9.dic', [[ - 1234 - foo - bar - ]]) - write_latin1('Xtest-sal.aff', [[ - SET ISO8859-1 - TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - MIDWORD '- - - KEP = - RAR ? - BAD ! - - PFX I N 1 - PFX I 0 in . - - PFX O Y 1 - PFX O 0 out . - - SFX S Y 2 - SFX S 0 s [^s] - SFX S 0 es s - - SFX N N 3 - SFX N 0 en [^n] - SFX N 0 nen n - SFX N 0 n . - - REP 3 - REP g ch - REP ch g - REP svp s.v.p. - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - - SAL AH(AEIOUY)-^ *H - SAL AR(AEIOUY)-^ *R - SAL A(HR)^ * - SAL A^ * - SAL AH(AEIOUY)- H - SAL AR(AEIOUY)- R - SAL A(HR) _ - SAL À^ * - SAL Å^ * - SAL BB- _ - SAL B B - SAL CQ- _ - SAL CIA X - SAL CH X - SAL C(EIY)- S - SAL CK K - SAL COUGH^ KF - SAL CC< C - SAL C K - SAL DG(EIY) K - SAL DD- _ - SAL D T - SAL É< E - SAL EH(AEIOUY)-^ *H - SAL ER(AEIOUY)-^ *R - SAL E(HR)^ * - SAL ENOUGH^$ *NF - SAL E^ * - SAL EH(AEIOUY)- H - SAL ER(AEIOUY)- R - SAL E(HR) _ - SAL FF- _ - SAL F F - SAL GN^ N - SAL GN$ N - SAL GNS$ NS - SAL GNED$ N - SAL GH(AEIOUY)- K - SAL GH _ - SAL GG9 K - SAL G K - SAL H H - SAL IH(AEIOUY)-^ *H - SAL IR(AEIOUY)-^ *R - SAL I(HR)^ * - SAL I^ * - SAL ING6 N - SAL IH(AEIOUY)- H - SAL IR(AEIOUY)- R - SAL I(HR) _ - SAL J K - SAL KN^ N - SAL KK- _ - SAL K K - SAL LAUGH^ LF - SAL LL- _ - SAL L L - SAL MB$ M - SAL MM M - SAL M M - SAL NN- _ - SAL N N - SAL OH(AEIOUY)-^ *H - SAL OR(AEIOUY)-^ *R - SAL O(HR)^ * - SAL O^ * - SAL OH(AEIOUY)- H - SAL OR(AEIOUY)- R - SAL O(HR) _ - SAL PH F - SAL PN^ N - SAL PP- _ - SAL P P - SAL Q K - SAL RH^ R - SAL ROUGH^ RF - SAL RR- _ - SAL R R - SAL SCH(EOU)- SK - SAL SC(IEY)- S - SAL SH X - SAL SI(AO)- X - SAL SS- _ - SAL S S - SAL TI(AO)- X - SAL TH @ - SAL TCH-- _ - SAL TOUGH^ TF - SAL TT- _ - SAL T T - SAL UH(AEIOUY)-^ *H - SAL UR(AEIOUY)-^ *R - SAL U(HR)^ * - SAL U^ * - SAL UH(AEIOUY)- H - SAL UR(AEIOUY)- R - SAL U(HR) _ - SAL V^ W - SAL V F - SAL WR^ R - SAL WH^ W - SAL W(AEIOU)- W - SAL X^ S - SAL X KS - SAL Y(AEIOU)- Y - SAL ZZ- _ - SAL Z S - ]]) - write_file('Xtest.utf-8.add', [[ - /regions=usgbnz - elequint/2 - elekwint/3 - ]]) - end) - - teardown(function() - os.remove('Xtest-sal.aff') - os.remove('Xtest.aff') - os.remove('Xtest.dic') - os.remove('Xtest.utf-8.add') - os.remove('Xtest.utf-8.add.spl') - os.remove('Xtest.utf-8.spl') - os.remove('Xtest.utf-8.sug') - os.remove('Xtest1.aff') - os.remove('Xtest1.dic') - os.remove('Xtest2.aff') - os.remove('Xtest3.aff') - os.remove('Xtest3.dic') - os.remove('Xtest4.aff') - os.remove('Xtest4.dic') - os.remove('Xtest5.aff') - os.remove('Xtest5.dic') - os.remove('Xtest6.aff') - os.remove('Xtest6.dic') - os.remove('Xtest7.aff') - os.remove('Xtest7.dic') - os.remove('Xtest8.aff') - os.remove('Xtest8.dic') - os.remove('Xtest9.aff') - os.remove('Xtest9.dic') - end) - - -- Function to test .aff/.dic with list of good and bad words. This was a - -- Vim function in the original legacy test. - local function test_one(aff, dic) - -- Generate a .spl file from a .dic and .aff file. - if helpers.iswin() then - os.execute('copy /y Xtest'..aff..'.aff Xtest.aff') - os.execute('copy /y Xtest'..dic..'.dic Xtest.dic') - else - os.execute('cp -f Xtest'..aff..'.aff Xtest.aff') - os.execute('cp -f Xtest'..dic..'.dic Xtest.dic') - end - source([[ - set spellfile= - function! SpellDumpNoShow() - " spelling scores depend on what happens to be drawn on screen - spelldump - %yank - quit - endfunction - $put ='' - $put ='test ]]..aff..'-'..dic..[[' - mkspell! Xtest Xtest - " Use that spell file. - set spl=Xtest.utf-8.spl spell - " List all valid words. - call SpellDumpNoShow() - $put - $put ='-------' - " Find all bad words and suggestions for them. - 1;/^]]..aff..[[good: - normal 0f:]s - let prevbad = '' - while 1 - let [bad, a] = spellbadword() - if bad == '' || bad == prevbad || bad == 'badend' - break - endif - let prevbad = bad - let lst = spellsuggest(bad, 3) - normal mm - $put =bad - $put =string(lst) - normal `m]s - endwhile - ]]) - end - - it('part 1-1', function() - insert([[ - 1good: wrong OK puts. Test the end - bad: inputs comment ok Ok. test déôl end the - badend - - test2: - elequint test elekwint test elekwent asdf - ]]) - test_one(1, 1) - feed_command([[$put =soundfold('goobledygoook')]]) - feed_command([[$put =soundfold('kóopërÿnôven')]]) - feed_command([[$put =soundfold('oeverloos gezwets edale')]]) - -- And now with SAL instead of SOFO items; test automatic reloading. - if helpers.iswin() then - os.execute('copy /y Xtest-sal.aff Xtest.aff') - else - os.execute('cp -f Xtest-sal.aff Xtest.aff') - end - feed_command('mkspell! Xtest Xtest') - feed_command([[$put =soundfold('goobledygoook')]]) - feed_command([[$put =soundfold('kóopërÿnôven')]]) - feed_command([[$put =soundfold('oeverloos gezwets edale')]]) - -- Also use an addition file. - feed_command('mkspell! Xtest.utf-8.add.spl Xtest.utf-8.add') - feed_command('set spellfile=Xtest.utf-8.add') - feed_command('/^test2:') - feed(']s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_us.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_gb.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_nz.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_ca.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('1,/^test 1-1/-1d') - expect([[ - test 1-1 - # file: Xtest.utf-8.spl - Comment - deol - déôr - input - OK - output - outputs - outtest - put - puts - test - testen - testn - the end - uk - wrong - ------- - bad - ['put', 'uk', 'OK'] - inputs - ['input', 'puts', 'outputs'] - comment - ['Comment', 'outtest', 'the end'] - ok - ['OK', 'uk', 'put'] - Ok - ['OK', 'Uk', 'Put'] - test - ['Test', 'testn', 'testen'] - déôl - ['deol', 'déôr', 'test'] - end - ['put', 'uk', 'test'] - the - ['put', 'uk', 'test'] - gebletegek - kepereneven - everles gesvets etele - kbltykk - kprnfn - *fls kswts tl - elekwent - elequint - elekwint - elekwint - elekwent - elequint - elekwent - elequint - elekwint]]) - end) - - it('part 2-1', function() - insert([[ - 2good: puts - bad: inputs comment ok Ok end the. test déôl - badend - ]]) - -- Postponed prefixes. - test_one(2, 1) - feed_command('1,/^test 2-1/-1d') - expect([=[ - test 2-1 - # file: Xtest.utf-8.spl - Comment - deol - déôr - OK - put - input - output - puts - outputs - test - outtest - testen - testn - the end - uk - wrong - ------- - bad - ['put', 'uk', 'OK'] - inputs - ['input', 'puts', 'outputs'] - comment - ['Comment'] - ok - ['OK', 'uk', 'put'] - Ok - ['OK', 'Uk', 'Put'] - end - ['put', 'uk', 'deol'] - the - ['put', 'uk', 'test'] - test - ['Test', 'testn', 'testen'] - déôl - ['deol', 'déôr', 'test']]=]) - end) - - it('part 3-3', function() - insert([[ - Test rules for compounding. - - 3good: foo mï foobar foofoobar barfoo barbarfoo - bad: bar la foomï barmï mïfoo mïbar mïmï lala mïla lamï foola labar - badend - ]]) - test_one(3, 3) - feed_command('1,/^test 3-3/-1d') - expect([=[ - test 3-3 - # file: Xtest.utf-8.spl - foo - mï - ------- - bad - ['foo', 'mï'] - bar - ['barfoo', 'foobar', 'foo'] - la - ['mï', 'foo'] - foomï - ['foo mï', 'foo', 'foofoo'] - barmï - ['barfoo', 'mï', 'barbar'] - mïfoo - ['mï foo', 'foo', 'foofoo'] - mïbar - ['foobar', 'barbar', 'mï'] - mïmï - ['mï mï', 'mï'] - lala - [] - mïla - ['mï', 'mï mï'] - lamï - ['mï', 'mï mï'] - foola - ['foo', 'foobar', 'foofoo'] - labar - ['barbar', 'foobar']]=]) - end) - - it('part 4-4', function() - insert([[ - Tests for compounding. - - 4good: word util bork prebork start end wordutil wordutils pro-ok - bork borkbork borkborkbork borkborkborkbork borkborkborkborkbork - tomato tomatotomato startend startword startwordword startwordend - startwordwordend startwordwordwordend prebork preborkbork - preborkborkbork - nouword - bad: wordutilize pro borkborkborkborkborkbork tomatotomatotomato - endstart endend startstart wordend wordstart - preborkprebork preborkpreborkbork - startwordwordwordwordend borkpreborkpreborkbork - utilsbork startnouword - badend - ]]) - test_one(4, 4) - feed_command('1,/^test 4-4/-1d') - expect([=[ - test 4-4 - # file: Xtest.utf-8.spl - bork - prebork - end - pro-ok - start - tomato - util - utilize - utils - word - nouword - ------- - bad - ['end', 'bork', 'word'] - wordutilize - ['word utilize', 'wordutils', 'wordutil'] - pro - ['bork', 'word', 'end'] - borkborkborkborkborkbork - ['bork borkborkborkborkbork', 'borkbork borkborkborkbork', 'borkborkbork borkborkbork'] - tomatotomatotomato - ['tomato tomatotomato', 'tomatotomato tomato', 'tomato tomato tomato'] - endstart - ['end start', 'start'] - endend - ['end end', 'end'] - startstart - ['start start'] - wordend - ['word end', 'word', 'wordword'] - wordstart - ['word start', 'bork start'] - preborkprebork - ['prebork prebork', 'preborkbork', 'preborkborkbork'] - preborkpreborkbork - ['prebork preborkbork', 'preborkborkbork', 'preborkborkborkbork'] - startwordwordwordwordend - ['startwordwordwordword end', 'startwordwordwordword', 'start wordwordwordword end'] - borkpreborkpreborkbork - ['bork preborkpreborkbork', 'bork prebork preborkbork', 'bork preborkprebork bork'] - utilsbork - ['utilbork', 'utils bork', 'util bork'] - startnouword - ['start nouword', 'startword', 'startborkword']]=]) - end) - - it('part 5-5', function() - insert([[ - Test affix flags with two characters - - 5good: fooa1 fooaé bar prebar barbork prebarbork startprebar - start end startend startmiddleend nouend - bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart - startprobar startnouend - badend - ]]) - test_one(5, 5) - feed_command('1,/^test 5-5/-1d') - expect([=[ - test 5-5 - # file: Xtest.utf-8.spl - bar - barbork - end - fooa1 - fooaé - nouend - prebar - prebarbork - start - ------- - bad - ['bar', 'end', 'fooa1'] - foo - ['fooa1', 'fooaé', 'bar'] - fooa2 - ['fooa1', 'fooaé', 'bar'] - prabar - ['prebar', 'bar', 'bar bar'] - probarbirk - ['prebarbork'] - middle - [] - startmiddle - ['startmiddleend', 'startmiddlebar'] - middleend - [] - endstart - ['end start', 'start'] - startprobar - ['startprebar', 'start prebar', 'startbar'] - startnouend - ['start nouend', 'startend']]=]) - end) - - it('part 6-6', function() - insert([[ - 6good: meea1 meeaé bar prebar barbork prebarbork leadprebar - lead end leadend leadmiddleend - bad: mee meea2 prabar probarbirk middle leadmiddle middleend endlead - leadprobar - badend - ]]) - test_one(6, 6) - feed_command('1,/^test 6-6/-1d') - expect([=[ - test 6-6 - # file: Xtest.utf-8.spl - bar - barbork - end - lead - meea1 - meeaé - prebar - prebarbork - ------- - bad - ['bar', 'end', 'lead'] - mee - ['meea1', 'meeaé', 'bar'] - meea2 - ['meea1', 'meeaé', 'lead'] - prabar - ['prebar', 'bar', 'leadbar'] - probarbirk - ['prebarbork'] - middle - [] - leadmiddle - ['leadmiddleend', 'leadmiddlebar'] - middleend - [] - endlead - ['end lead', 'lead', 'end end'] - leadprobar - ['leadprebar', 'lead prebar', 'leadbar']]=]) - end) - - it('part 7-7', function() - insert([[ - 7good: meea1 meeaé bar prebar barmeat prebarmeat leadprebar - lead tail leadtail leadmiddletail - bad: mee meea2 prabar probarmaat middle leadmiddle middletail taillead - leadprobar - badend - ]]) - -- Compound words. - test_one(7, 7) - -- Assert buffer contents. - feed_command('1,/^test 7-7/-1d') - expect([=[ - test 7-7 - # file: Xtest.utf-8.spl - bar - barmeat - lead - meea1 - meeaé - prebar - prebarmeat - tail - ------- - bad - ['bar', 'lead', 'tail'] - mee - ['meea1', 'meeaé', 'bar'] - meea2 - ['meea1', 'meeaé', 'lead'] - prabar - ['prebar', 'bar', 'leadbar'] - probarmaat - ['prebarmeat'] - middle - [] - leadmiddle - ['leadmiddlebar'] - middletail - [] - taillead - ['tail lead', 'tail'] - leadprobar - ['leadprebar', 'lead prebar', 'leadbar']]=]) - end) - - it('part 8-8', function() - insert([[ - 8good: foo bar faabar - bad: foobar barfoo - badend - ]]) - -- NOSPLITSUGS - test_one(8, 8) - -- Assert buffer contents. - feed_command('1,/^test 8-8/-1d') - expect([=[ - test 8-8 - # file: Xtest.utf-8.spl - bar - faabar - foo - ------- - bad - ['bar', 'foo'] - foobar - ['faabar', 'foo bar', 'bar'] - barfoo - ['bar foo', 'bar', 'foo']]=]) - end) - - it('part 9-9', function() - insert([[ - 9good: 0b1011 0777 1234 0x01ff - badend - ]]) - -- NOSPLITSUGS - test_one(9, 9) - -- Assert buffer contents. - feed_command('1,/^test 9-9/-1d') - expect([=[ - test 9-9 - # file: Xtest.utf-8.spl - bar - foo - -------]=]) - end) -end) diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua index 466e3ed830..37a94476a0 100644 --- a/test/functional/legacy/autochdir_spec.lua +++ b/test/functional/legacy/autochdir_spec.lua @@ -1,10 +1,11 @@ local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) -local clear, eq = helpers.clear, helpers.eq -local eval, command = helpers.eval, helpers.command +local clear, eq, matches = helpers.clear, helpers.eq, helpers.matches +local eval, command, call = helpers.eval, helpers.command, helpers.call +local exec_capture = helpers.exec_capture describe('autochdir behavior', function() - local dir = 'Xtest-functional-legacy-autochdir' + local dir = 'Xtest_functional_legacy_autochdir' before_each(function() lfs.mkdir(dir) @@ -23,4 +24,35 @@ describe('autochdir behavior', function() eq('Xtest', eval("expand('%')")) eq(dir, eval([[substitute(getcwd(), '.*[/\\]\(\k*\)', '\1', '')]])) end) + + it(':verbose pwd shows whether autochdir is used', function() + local subdir = 'Xautodir' + command('cd '..dir) + local cwd = eval('getcwd()') + command('edit global.txt') + matches('%[global%].*'..dir, exec_capture('verbose pwd')) + call('mkdir', subdir) + command('split '..subdir..'/local.txt') + command('lcd '..subdir) + matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + command('set autochdir') + command('wincmd w') + matches('%[autochdir%].*'..dir, exec_capture('verbose pwd')) + command('lcd '..cwd) + matches('%[window%].*'..dir, exec_capture('verbose pwd')) + command('tcd '..cwd) + matches('%[tabpage%].*'..dir, exec_capture('verbose pwd')) + command('cd '..cwd) + matches('%[global%].*'..dir, exec_capture('verbose pwd')) + command('edit') + matches('%[autochdir%].*'..dir, exec_capture('verbose pwd')) + command('wincmd w') + matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + command('set noautochdir') + matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + command('wincmd w') + matches('%[global%].*'..dir, exec_capture('verbose pwd')) + command('wincmd w') + matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + end) end) diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 1914818215..5e586d3a6a 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local nvim = helpers.meths -local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq +local clear, eq, neq, eval = helpers.clear, helpers.eq, helpers.neq, helpers.eval local curbuf, buf = helpers.curbuf, helpers.bufmeths local curwin = helpers.curwin local exec_capture = helpers.exec_capture @@ -10,11 +10,14 @@ local function declare_hook_function() source([[ fu! AutoCommand(match, bufnr, winnr) let l:acc = { - \ 'option' : a:match, - \ 'oldval' : v:option_old, - \ 'newval' : v:option_new, - \ 'scope' : v:option_type, - \ 'attr' : { + \ 'option' : a:match, + \ 'oldval' : v:option_old, + \ 'oldval_l' : v:option_oldlocal, + \ 'oldval_g' : v:option_oldglobal, + \ 'newval' : v:option_new, + \ 'scope' : v:option_type, + \ 'cmd' : v:option_command, + \ 'attr' : { \ 'bufnr' : a:bufnr, \ 'winnr' : a:winnr, \ } @@ -42,13 +45,16 @@ local function get_result() return ret end -local function expected_table(option, oldval, newval, scope, attr) +local function expected_table(option, oldval, oldval_l, oldval_g, newval, scope, cmd, attr) return { - option = option, - oldval = tostring(oldval), - newval = tostring(newval), - scope = scope, - attr = attr, + option = option, + oldval = tostring(oldval), + oldval_l = tostring(oldval_l), + oldval_g = tostring(oldval_g), + newval = tostring(newval), + scope = scope, + cmd = cmd, + attr = attr, } end @@ -66,7 +72,7 @@ local function expected_combination(...) end for i, v in ipairs(args) do - local attr = v[5] + local attr = v[8] if not attr then -- remove attr entries ret[i].attr = nil @@ -112,7 +118,7 @@ local function get_new_window_number() end describe('au OptionSet', function() - describe('with any opton (*)', function() + describe('with any option (*)', function() before_each(function() clear() @@ -123,44 +129,44 @@ describe('au OptionSet', function() it('should be called in setting number option', function() command('set nu') - expected_combination({'number', 0, 1, 'global'}) + expected_combination({'number', 0, 0, 0, 1, 'global', 'set'}) command('setlocal nonu') - expected_combination({'number', 1, 0, 'local'}) + expected_combination({'number', 1, 1, '', 0, 'local', 'setlocal'}) command('setglobal nonu') - expected_combination({'number', 1, 0, 'global'}) + expected_combination({'number', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ai') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, '', 0, 1, 'global', 'setglobal'}) command('set noai') - expected_combination({'autoindent', 1, 0, 'global'}) + expected_combination({'autoindent', 1, 1, 1, 0, 'global', 'set'}) end) it('should be called in inverting global autoindent option',function() command('set ai!') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, 0, 0, 1, 'global', 'set'}) end) it('should be called in being unset local autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setlocal ai<') - expected_combination({'autoindent', 1, 0, 'local'}) + expected_combination({'autoindent', 1, 1, '', 0, 'local', 'setlocal'}) end) it('should be called in setting global list and number option at the same time',function() command('set list nu') expected_combination( - {'list', 0, 1, 'global'}, - {'number', 0, 1, 'global'} + {'list', 0, 0, 0, 1, 'global', 'set'}, + {'number', 0, 0, 0, 1, 'global', 'set'} ) end) @@ -171,25 +177,27 @@ describe('au OptionSet', function() it('should be called in setting local acd', function() command('setlocal acd') - expected_combination({'autochdir', 0, 1, 'local'}) + expected_combination({'autochdir', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in setting autoread', function() command('set noar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, 1, 1, 0, 'global', 'set'}) command('setlocal ar') - expected_combination({'autoread', 0, 1, 'local'}) + expected_combination({'autoread', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in inverting global autoread', function() command('setglobal invar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting backspace option through :let', function() + local oldval = eval('&backspace') + command('let &bs=""') - expected_combination({'backspace', 'indent,eol,start', '', 'global'}) + expected_combination({'backspace', oldval, oldval, oldval, '', 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -200,7 +208,7 @@ describe('au OptionSet', function() it('should trigger using correct option name', function() command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -208,9 +216,339 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) + + it('with string global option', function() + local oldval = eval('&backupext') + + command('set backupext=foo') + expected_combination({'backupext', oldval, oldval, oldval, 'foo', 'global', 'set'}) + + command('set backupext&') + expected_combination({'backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal backupext=bar') + expected_combination({'backupext', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set backupext&') + -- As this is a global option this sets the global value even though :setlocal is used! + command('setlocal backupext=baz') + expected_combination({'backupext', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal backupext=ext_global') + command('noa setlocal backupext=ext_local') -- Sets the global(!) value + command('set backupext=foo') + expected_combination({ + 'backupext', 'ext_local', 'ext_local', 'ext_local', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to buffer) option', function() + local oldval = eval('&tags') + + command('set tags=tagpath') + expected_combination({'tags', oldval, oldval, oldval, 'tagpath', 'global', 'set'}) + + command('set tags&') + expected_combination({'tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set'}) + + command('setglobal tags=tagpath1') + expected_combination({'tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal'}) + + command('setlocal tags=tagpath2') + expected_combination({'tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal tags=tag_global') + command('noa setlocal tags=tag_local') + command('set tags=tagpath') + expected_combination({ + 'tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set' + }) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa set tags=tag_global') + command('noa setlocal tags=') + command('set tags=tagpath') + expected_combination({'tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set'}) + end) + + it('with string local (to buffer) option', function() + local oldval = eval('&spelllang') + + command('set spelllang=elvish,klingon') + expected_combination({'spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set'}) + + command('set spelllang&') + expected_combination({ + 'spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set' + }) + + command('setglobal spelllang=elvish') + expected_combination({'spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal'}) + + command('noa set spelllang&') + command('setlocal spelllang=klingon') + expected_combination({'spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal spelllang=spellglobal') + command('noa setlocal spelllang=spelllocal') + command('set spelllang=foo') + expected_combination({ + 'spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to window) option', function() + local oldval = eval('&statusline') + + command('set statusline=foo') + expected_combination({'statusline', oldval, oldval, '', 'foo', 'global', 'set'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('set statusline&') + expected_combination({'statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal statusline=bar') + expected_combination({'statusline', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set statusline&') + command('setlocal statusline=baz') + expected_combination({'statusline', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal statusline=bar') + command('noa setlocal statusline=baz') + command('set statusline=foo') + expected_combination({'statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set'}) + end) + + it('with string local (to window) option', function() + local oldval = eval('&foldignore') + + command('set foldignore=fo') + expected_combination({'foldignore', oldval, oldval, oldval, 'fo', 'global', 'set'}) + + command('set foldignore&') + expected_combination({'foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set'}) + + command('setglobal foldignore=bar') + expected_combination({'foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set foldignore&') + command('setlocal foldignore=baz') + expected_combination({'foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal foldignore=glob') + command('noa setlocal foldignore=loc') + command('set foldignore=fo') + expected_combination({'foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set'}) + end) + + it('with number global option', function() + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setglobal cmdheight=2') + expected_combination({'cmdheight', 1, '', 1, 2, 'global', 'setglobal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setlocal cmdheight=2') + expected_combination({'cmdheight', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('set cmdheight=2') + expected_combination({'cmdheight', 1, 1, 1, 2, 'global', 'set'}) + + command('noa set cmdheight=8') + command('set cmdheight=2') + expected_combination({'cmdheight', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to buffer) option', function() + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setglobal undolevels=2') + expected_combination({'undolevels', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setlocal undolevels=2') + expected_combination({'undolevels', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('set undolevels=2') + expected_combination({'undolevels', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set undolevels=8') + command('set undolevels=2') + expected_combination({'undolevels', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to buffer) option', function() + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setglobal wrapmargin=2') + expected_combination({'wrapmargin', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setlocal wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set wrapmargin=8') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to window) option', function() + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setglobal scrolloff=2') + expected_combination({'scrolloff', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setlocal scrolloff=2') + expected_combination({'scrolloff', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('set scrolloff=2') + expected_combination({'scrolloff', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set scrolloff=8') + command('set scrolloff=2') + expected_combination({'scrolloff', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to window) option', function() + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setglobal foldcolumn=2') + expected_combination({'foldcolumn', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setlocal foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set foldcolumn=8') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with boolean global option', function() + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setglobal nowrapscan') + expected_combination({'wrapscan', 1, '', 1, 0, 'global', 'setglobal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setlocal nowrapscan') + expected_combination({'wrapscan', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('set nowrapscan') + expected_combination({'wrapscan', 1, 1, 1, 0, 'global', 'set'}) + + command('noa set nowrapscan') + command('set wrapscan') + expected_combination({'wrapscan', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean global-local (to buffer) option', function() + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setglobal autoread') + expected_combination({'autoread', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setlocal noautoread') + expected_combination({'autoread', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('set autoread') + expected_combination({'autoread', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set noautoread') + command('set autoread') + expected_combination({'autoread', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to buffer) option', function() + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setglobal cindent') + expected_combination({'cindent', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setlocal nocindent') + expected_combination({'cindent', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('set cindent') + expected_combination({'cindent', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocindent') + command('set cindent') + expected_combination({'cindent', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to window) option', function() + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setglobal cursorcolumn') + expected_combination({'cursorcolumn', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setlocal nocursorcolumn') + expected_combination({'cursorcolumn', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with option value converted internally', function() + command('noa set backspace=1') + command('set backspace=2') + expected_combination(({ + 'backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set' + })) + end) end) describe('with specific option', function() @@ -228,13 +566,13 @@ describe('au OptionSet', function() expected_empty() command('setlocal ro') - expected_combination({'readonly', 0, 1, 'local'}) + expected_combination({'readonly', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ro') - expected_combination({'readonly', 0, 1, 'global'}) + expected_combination({'readonly', 0, '', 0, 1, 'global', 'setglobal'}) command('set noro') - expected_combination({'readonly', 1, 0, 'global'}) + expected_combination({'readonly', 1, 1, 1, 0, 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -249,7 +587,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -259,7 +597,7 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) @@ -275,7 +613,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setwinvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should not trigger if the current window is different from the targetted window', function() @@ -295,7 +633,7 @@ describe('au OptionSet', function() nvim.set_option('autochdir', true) eq(true, nvim.get_option('autochdir')) - expected_combination({'autochdir', '0', '1', 'global'}) + expected_combination({'autochdir', 0, '', 0, 1, 'global', 'setglobal'}) end) it('should trigger if a number option be set globally', function() @@ -303,7 +641,7 @@ describe('au OptionSet', function() nvim.set_option('cmdheight', 5) eq(5, nvim.get_option('cmdheight')) - expected_combination({'cmdheight', 1, 5, 'global'}) + expected_combination({'cmdheight', 1, '', 1, 5, 'global', 'setglobal'}) end) it('should trigger if a string option be set globally', function() @@ -311,7 +649,7 @@ describe('au OptionSet', function() nvim.set_option('ambiwidth', 'double') eq('double', nvim.get_option('ambiwidth')) - expected_combination({'ambiwidth', 'single', 'double', 'global'}) + expected_combination({'ambiwidth', 'single', '', 'single', 'double', 'global', 'setglobal'}) end) end) end) diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index 6a5538c26f..6262db3a2f 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -29,7 +29,7 @@ describe('filename modifiers', function() call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) call assert_equal(fnamemodify(tmpdir, ':~').'/test.out', fnamemodify('test.out', ':~')) - call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) + call assert_equal(fnamemodify(tmpdir, ':~').'/../testdir/a', fnamemodify('../testdir/a', ':~')) call assert_equal('a', fnamemodify('../testdir/a', ':t')) call assert_equal('', fnamemodify('.', ':p:t')) call assert_equal('test.out', fnamemodify('test.out', ':p:t')) diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index 48cd3ef9f8..4f9f5a0237 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -20,6 +20,9 @@ describe('packadd', function() func SetUp() let s:topdir = expand(getcwd() . '/Xdir') + if isdirectory(s:topdir) + call delete(s:topdir, 'rf') + endif exe 'set packpath=' . s:topdir let s:plugdir = expand(s:topdir . '/pack/mine/opt/mytest') endfunc diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua index 513be807be..47eca19de3 100644 --- a/test/functional/legacy/prompt_buffer_spec.lua +++ b/test/functional/legacy/prompt_buffer_spec.lua @@ -1,9 +1,12 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local feed= helpers.feed +local feed = helpers.feed local source = helpers.source local clear = helpers.clear local feed_command = helpers.feed_command +local poke_eventloop = helpers.poke_eventloop +local meths = helpers.meths +local eq = helpers.eq describe('prompt buffer', function() local screen @@ -28,12 +31,17 @@ describe('prompt buffer', function() func TimerFunc(text) call append(line("$") - 1, 'Result: "' . a:text .'"') endfunc + + func SwitchWindows() + call timer_start(0, {-> execute("wincmd p|wincmd p", "")}) + endfunc ]]) feed_command("set noshowmode | set laststatus=0") feed_command("call setline(1, 'other buffer')") feed_command("new") feed_command("set buftype=prompt") feed_command("call prompt_setcallback(bufnr(''), function('TextEntered'))") + feed_command("eval bufnr('')->prompt_setprompt('cmd: ')") end) after_each(function() @@ -56,10 +64,10 @@ describe('prompt buffer', function() feed("i") feed("hello\n") screen:expect([[ - % hello | + cmd: hello | Command: "hello" | Result: "hello" | - % ^ | + cmd: ^ | [Prompt] [+] | other buffer | ~ | @@ -98,7 +106,7 @@ describe('prompt buffer', function() feed("i") feed("hello<BS><BS>") screen:expect([[ - % hel^ | + cmd: hel^ | ~ | ~ | ~ | @@ -111,7 +119,20 @@ describe('prompt buffer', function() ]]) feed("<Left><Left><Left><BS>-") screen:expect([[ - % -^hel | + cmd: -^hel | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + | + ]]) + feed("<C-O>lz") + screen:expect([[ + cmd: -hz^el | ~ | ~ | ~ | @@ -124,7 +145,7 @@ describe('prompt buffer', function() ]]) feed("<End>x") screen:expect([[ - % -helx^ | + cmd: -hzelx^ | ~ | ~ | ~ | @@ -150,4 +171,58 @@ describe('prompt buffer', function() ]]) end) + it('switch windows', function() + feed_command("set showmode") + feed("i") + screen:expect([[ + cmd: ^ | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + -- INSERT -- | + ]]) + feed("<C-O>:call SwitchWindows()<CR>") + poke_eventloop() + screen:expect([[ + cmd: ^ | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + -- INSERT -- | + ]]) + feed("<Esc>") + poke_eventloop() + screen:expect([[ + cmd:^ | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + | + ]]) + end) + + it('keeps insert mode after aucmd_restbuf in callback', function() + source [[ + let s:buf = nvim_create_buf(1, 1) + call timer_start(0, {-> nvim_buf_set_lines(s:buf, -1, -1, 0, ['walrus'])}) + startinsert + ]] + poke_eventloop() + eq({ mode = "i", blocking = false }, meths.get_mode()) + end) end) diff --git a/test/functional/legacy/searchpos_spec.lua b/test/functional/legacy/searchpos_spec.lua index 60f1edcd3f..fc18341c38 100644 --- a/test/functional/legacy/searchpos_spec.lua +++ b/test/functional/legacy/searchpos_spec.lua @@ -17,7 +17,7 @@ describe('searchpos', function() call('cursor', 1, 1) eq({1, 1, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) call('cursor', 1, 2) - eq({2, 1, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + eq({2, 1, 1}, eval([['\%(\([a-z]\)\|\_.\)\{-}xyz'->searchpos('pcW')]])) command('set cpo-=c') call('cursor', 1, 2) diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index ec53916024..a88da63e90 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -110,20 +110,35 @@ describe('vim.diagnostic', function() it('retrieves diagnostics from all buffers and namespaces', function() local result = exec_lua [[ - vim.diagnostic.set(diagnostic_ns, 1, { + local other_bufnr = vim.api.nvim_create_buf(true, false) + local lines = vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, true) + vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 2, 1, 2, 1), }) - vim.diagnostic.set(other_ns, 2, { + vim.diagnostic.set(other_ns, other_bufnr, { make_error('Diagnostic #3', 3, 1, 3, 1), }) return vim.diagnostic.get() ]] eq(3, #result) - eq(2, exec_lua([[return #vim.tbl_filter(function(d) return d.bufnr == 1 end, ...)]], result)) + eq(2, exec_lua([[return #vim.tbl_filter(function(d) return d.bufnr == diagnostic_bufnr end, ...)]], result)) eq('Diagnostic #1', result[1].message) end) + it('resolves buffer number 0 to the current buffer', function() + eq(2, exec_lua [[ + vim.api.nvim_set_current_buf(diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 2, 1, 2, 1), + }) + return #vim.diagnostic.get(0) + ]]) + end) + it('saves and count a single error', function() eq(1, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { @@ -271,6 +286,8 @@ describe('vim.diagnostic', function() describe('show() and hide()', function() it('works', function() local result = exec_lua [[ + local other_bufnr = vim.api.nvim_create_buf(true, false) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) local result = {} @@ -284,36 +301,289 @@ describe('vim.diagnostic', function() local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 5), } + local other_buffer_diags = { + make_info("This is interesting", 0, 0, 0, 0) + } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) - -- Both - table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) + -- All buffers and namespaces + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) -- Hide one namespace vim.diagnostic.hide(diagnostic_ns) - table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns)) + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) -- Show one namespace vim.diagnostic.show(diagnostic_ns) - table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns)) + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + -- Hide one buffer + vim.diagnostic.hide(nil, other_bufnr) + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) - -- Hide all namespaces + -- Hide everything vim.diagnostic.hide() - table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + -- Show one buffer + vim.diagnostic.show(nil, diagnostic_bufnr) + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + return result + ]] + + eq(4, result[1]) + eq(1, result[2]) + eq(4, result[3]) + eq(3, result[4]) + eq(0, result[5]) + eq(3, result[6]) + end) + + it("doesn't error after bwipeout on buffer", function() + exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {{ lnum = 0, end_lnum = 0, col = 0, end_col = 0 }}) + vim.cmd("bwipeout! " .. diagnostic_bufnr) + + vim.diagnostic.show(diagnostic_ns) + vim.diagnostic.hide(diagnostic_ns) + ]] + end) + end) - -- Show all namespaces - vim.diagnostic.show() - table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) + describe('enable() and disable()', function() + it('works without arguments', function() + local result = exec_lua [[ + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + + local result = {} + + vim.diagnostic.config({ underline = false, virtual_text = true }) + + local ns_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local ns_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns)) + + vim.diagnostic.disable() + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns)) + + -- Create a new buffer + local other_bufnr = vim.api.nvim_create_buf(true, false) + local other_buffer_diags = { + make_info("This is interesting", 0, 0, 0, 0) + } + + vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + vim.diagnostic.enable() + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) return result ]] eq(3, result[1]) eq(0, result[2]) - eq(2, result[3]) - eq(0, result[4]) + eq(0, result[3]) + eq(4, result[4]) + end) + + it('works with only a buffer argument', function() + local result = exec_lua [[ + local other_bufnr = vim.api.nvim_create_buf(true, false) + + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + + local result = {} + + vim.diagnostic.config({ underline = false, virtual_text = true }) + + local ns_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local ns_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + local other_buffer_diags = { + make_info("This is interesting", 0, 0, 0, 0) + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + vim.diagnostic.disable(diagnostic_bufnr) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + vim.diagnostic.enable(diagnostic_bufnr) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + vim.diagnostic.disable(other_bufnr) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + return result + ]] + + eq(4, result[1]) + eq(1, result[2]) + eq(4, result[3]) + eq(3, result[4]) + end) + + it('works with only a namespace argument', function() + local result = exec_lua [[ + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + + local result = {} + + vim.diagnostic.config({ underline = false, virtual_text = true }) + + local ns_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local ns_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns)) + + vim.diagnostic.disable(nil, diagnostic_ns) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns)) + + vim.diagnostic.enable(nil, diagnostic_ns) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns)) + + vim.diagnostic.disable(nil, other_ns) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns)) + + return result + ]] + + eq(3, result[1]) + eq(1, result[2]) + eq(3, result[3]) + eq(2, result[4]) + end) + + it('works with both a buffer and a namespace argument', function() + local result = exec_lua [[ + local other_bufnr = vim.api.nvim_create_buf(true, false) + + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + + local result = {} + + vim.diagnostic.config({ underline = false, virtual_text = true }) + + local ns_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local ns_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + local other_buffer_diags = { + make_info("This is interesting", 0, 0, 0, 0) + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + vim.diagnostic.disable(diagnostic_bufnr, other_ns) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + -- Should have no effect + vim.diagnostic.disable(other_bufnr, other_ns) + + table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + + count_extmarks(diagnostic_bufnr, other_ns) + + count_extmarks(other_bufnr, diagnostic_ns)) + + return result + ]] + + eq(4, result[1]) + eq(2, result[2]) + eq(1, result[3]) + eq(3, result[4]) eq(3, result[5]) end) end) @@ -380,6 +650,15 @@ describe('vim.diagnostic', function() ]]) end) + + it("doesn't error after bwipeout called on buffer", function() + exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {{ lnum = 0, end_lnum = 0, col = 0, end_col = 0 }}) + vim.cmd("bwipeout! " .. diagnostic_bufnr) + + vim.diagnostic.reset(diagnostic_ns) + ]] + end) end) describe('get_next_pos()', function() @@ -437,6 +716,19 @@ describe('vim.diagnostic', function() return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns } ]]) end) + + it('works with diagnostics past the end of the line #16349', function() + eq({4, 0}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 3, 9001, 3, 9001), + make_error('Diagnostic #2', 4, 0, 4, 0), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {1, 1}) + vim.diagnostic.goto_next { float = false } + return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } + ]]) + end) end) describe('get_prev_pos()', function() @@ -633,7 +925,7 @@ describe('vim.diagnostic', function() ]] eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) - -- eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) end) it('allows filtering by severity', function() @@ -1032,6 +1324,44 @@ describe('vim.diagnostic', function() end) describe('open_float()', function() + it('can display a header', function() + eq({'Diagnostics:', '1. Syntax error'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float() + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + + eq({"We're no strangers to love...", '1. Syntax error'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float({header = "We're no strangers to love..."}) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + + eq({'You know the rules', '1. Syntax error'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float({header = {'You know the rules', 'Search'}}) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + end) + it('can show diagnostics from the whole buffer', function() eq({'1. Syntax error', '2. Some warning'}, exec_lua [[ local diagnostics = { @@ -1040,7 +1370,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header = false}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope="buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1057,7 +1387,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {2, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="line"}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1072,7 +1402,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="line", pos=1}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, pos=1}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1089,7 +1419,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {2, 2}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor"}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1104,7 +1434,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor", pos={1,3}}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={1,3}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1119,7 +1449,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor", pos={0,first_line_len}}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={0,first_line_len}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1136,7 +1466,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {scope="line"}) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines @@ -1189,7 +1519,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {show_header = false}) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {header = false}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines @@ -1203,7 +1533,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {show_header = false, scope = "line", pos = 5}) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {header = false, pos = 5}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines @@ -1219,7 +1549,7 @@ describe('vim.diagnostic', function() } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { - show_header = false, + header = false, source = "if_many", }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) @@ -1229,7 +1559,7 @@ describe('vim.diagnostic', function() eq({"1. source x: Syntax error"}, exec_lua [[ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { - show_header = false, + header = false, source = "always", }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) @@ -1244,7 +1574,7 @@ describe('vim.diagnostic', function() } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { - show_header = false, + header = false, source = "if_many", }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) @@ -1268,7 +1598,7 @@ describe('vim.diagnostic', function() vim.diagnostic.config({severity_sort = false}) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false }) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1276,7 +1606,7 @@ describe('vim.diagnostic', function() eq({"1. Syntax error", "2. Error", "3. Warning", "4. Info"}, exec_lua [[ vim.diagnostic.config({severity_sort = true}) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false }) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1284,7 +1614,7 @@ describe('vim.diagnostic', function() eq({"1. Info", "2. Warning", "3. Error", "4. Syntax error"}, exec_lua [[ vim.diagnostic.config({severity_sort = { reverse = true } }) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false }) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1308,7 +1638,7 @@ describe('vim.diagnostic', function() make_warning('Warning', 0, 0, 0, 1), }) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false }) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) if not float_bufnr then return 0 end @@ -1335,7 +1665,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header = false, number = "always"}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1348,7 +1678,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header = false, prefix = ""}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer", prefix = ""}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1357,12 +1687,12 @@ describe('vim.diagnostic', function() eq({'1. Syntax error', '2. Some warning'}, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), - make_warning("Some warning", 1, 1, 1, 3), + make_warning("Some warning", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, { - show_header = false, + local float_bufnr, winnr = vim.diagnostic.open_float({ + header = false, prefix = function(_, i, total) -- Only show a number if there is more than one diagnostic if total > 1 then @@ -1382,8 +1712,8 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, { - show_header = false, + local float_bufnr, winnr = vim.diagnostic.open_float({ + header = false, prefix = function(_, i, total) -- Only show a number if there is more than one diagnostic if total > 1 then @@ -1397,8 +1727,22 @@ describe('vim.diagnostic', function() return lines ]]) - eq("Error executing lua: .../diagnostic.lua:0: prefix: expected 'string' or 'function', got 42", - pcall_err(exec_lua, [[ vim.diagnostic.open_float(0, { prefix = 42 }) ]])) + eq("Error executing lua: .../diagnostic.lua:0: prefix: expected 'string' or 'table' or 'function', got 42", + pcall_err(exec_lua, [[ vim.diagnostic.open_float({ prefix = 42 }) ]])) + end) + + it('works with the old signature', function() + eq({'1. Syntax error'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float(0, { header = false }) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) end) end) @@ -1588,5 +1932,27 @@ describe('vim.diagnostic', function() return {show_called, hide_called} ]]) end) + + it('triggers the autocommand when diagnostics are set', function() + eq(1, exec_lua [[ + vim.g.diagnostic_autocmd_triggered = 0 + vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1') + vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic', 0, 0, 0, 0) + }) + return vim.g.diagnostic_autocmd_triggered + ]]) + end) + + it('triggers the autocommand when diagnostics are cleared', function() + eq(1, exec_lua [[ + vim.g.diagnostic_autocmd_triggered = 0 + vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1') + vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") + vim.diagnostic.reset(diagnostic_ns, diagnostic_bufnr) + return vim.g.diagnostic_autocmd_triggered + ]]) + end) end) end) diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index 81f1820986..dbfbe2dbfe 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -143,8 +143,8 @@ describe('URI methods', function() end) it('uri_to_fname returns non-file scheme URI without authority unchanged', function() - eq('zipfile:/path/to/archive.zip%3A%3Afilename.txt', exec_lua [[ - return vim.uri_to_fname('zipfile:/path/to/archive.zip%3A%3Afilename.txt') + eq('zipfile:///path/to/archive.zip%3A%3Afilename.txt', exec_lua [[ + return vim.uri_to_fname('zipfile:///path/to/archive.zip%3A%3Afilename.txt') ]]) end) end) @@ -186,7 +186,7 @@ describe('URI methods', function() end) it('uri_to_bufnr & uri_from_bufnr returns original uri for non-file uris without authority', function() - local uri = 'zipfile:/path/to/archive.zip%3A%3Afilename.txt' + local uri = 'zipfile:///path/to/archive.zip%3A%3Afilename.txt' local test_case = string.format([[ local uri = '%s' return vim.uri_from_bufnr(vim.uri_to_bufnr(uri)) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 3832d27a22..317f92fcdc 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -131,9 +131,9 @@ describe('lua stdlib', function() eq(false, funcs.luaeval('vim.startswith("123", "2")')) eq(false, funcs.luaeval('vim.startswith("123", "1234")')) - eq("Error executing lua: vim/shared.lua:0: prefix: expected string, got nil", + matches("prefix: expected string, got nil", pcall_err(exec_lua, 'return vim.startswith("123", nil)')) - eq("Error executing lua: vim/shared.lua:0: s: expected string, got nil", + matches("s: expected string, got nil", pcall_err(exec_lua, 'return vim.startswith(nil, "123")')) end) @@ -147,9 +147,9 @@ describe('lua stdlib', function() eq(false, funcs.luaeval('vim.endswith("123", "2")')) eq(false, funcs.luaeval('vim.endswith("123", "1234")')) - eq("Error executing lua: vim/shared.lua:0: suffix: expected string, got nil", + matches("suffix: expected string, got nil", pcall_err(exec_lua, 'return vim.endswith("123", nil)')) - eq("Error executing lua: vim/shared.lua:0: s: expected string, got nil", + matches("s: expected string, got nil", pcall_err(exec_lua, 'return vim.endswith(nil, "123")')) end) @@ -220,9 +220,9 @@ describe('lua stdlib', function() eq({"yy","xx"}, exec_lua("return test_table")) -- Validates args. - eq('Error executing lua: vim.schedule: expected function', + matches('vim.schedule: expected function', pcall_err(exec_lua, "vim.schedule('stringly')")) - eq('Error executing lua: vim.schedule: expected function', + matches('vim.schedule: expected function', pcall_err(exec_lua, "vim.schedule()")) exec_lua([[ @@ -232,7 +232,7 @@ describe('lua stdlib', function() ]]) feed("<cr>") - eq('Error executing vim.schedule lua callback: [string "<nvim>"]:2: big failure\nvery async', remove_trace(eval("v:errmsg"))) + matches('big failure\nvery async', remove_trace(eval("v:errmsg"))) local screen = Screen.new(60,5) screen:set_default_attr_ids({ @@ -300,16 +300,16 @@ describe('lua stdlib', function() } for _, t in ipairs(loops) do - eq("Error executing lua: vim/shared.lua:0: Infinite loop detected", pcall_err(split, t[1], t[2])) + matches("Infinite loop detected", pcall_err(split, t[1], t[2])) end -- Validates args. eq(true, pcall(split, 'string', 'string')) - eq('Error executing lua: vim/shared.lua:0: s: expected string, got number', + matches('s: expected string, got number', pcall_err(split, 1, 'string')) - eq('Error executing lua: vim/shared.lua:0: sep: expected string, got number', + matches('sep: expected string, got number', pcall_err(split, 'string', 1)) - eq('Error executing lua: vim/shared.lua:0: kwargs: expected table, got number', + matches('kwargs: expected table, got number', pcall_err(split, 'string', 'string', 1)) end) @@ -330,7 +330,7 @@ describe('lua stdlib', function() end -- Validates args. - eq('Error executing lua: vim/shared.lua:0: s: expected string, got number', + matches('s: expected string, got number', pcall_err(trim, 2)) end) @@ -396,7 +396,21 @@ describe('lua stdlib', function() return t1.f() ~= t2.f() ]])) - eq('Error executing lua: vim/shared.lua:0: Cannot deepcopy object of type thread', + ok(exec_lua([[ + local t1 = {a = 5} + t1.self = t1 + local t2 = vim.deepcopy(t1) + return t2.self == t2 and t2.self ~= t1 + ]])) + + ok(exec_lua([[ + local mt = {mt=true} + local t1 = setmetatable({a = 5}, mt) + local t2 = vim.deepcopy(t1) + return getmetatable(t2) == mt + ]])) + + matches('Cannot deepcopy object of type thread', pcall_err(exec_lua, [[ local thread = coroutine.create(function () return 0 end) local t = {thr = thread} @@ -409,7 +423,7 @@ describe('lua stdlib', function() eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) -- Validates args. - eq('Error executing lua: vim/shared.lua:0: s: expected string, got number', + matches('s: expected string, got number', pcall_err(exec_lua, [[return vim.pesc(2)]])) end) @@ -534,19 +548,19 @@ describe('lua stdlib', function() return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1 ]])) - eq('Error executing lua: vim/shared.lua:0: invalid "behavior": nil', + matches('invalid "behavior": nil', pcall_err(exec_lua, [[ return vim.tbl_extend() ]]) ) - eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 1, expected at least 3)', + matches('wrong number of arguments %(given 1, expected at least 3%)', pcall_err(exec_lua, [[ return vim.tbl_extend("keep") ]]) ) - eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 2, expected at least 3)', + matches('wrong number of arguments %(given 2, expected at least 3%)', pcall_err(exec_lua, [[ return vim.tbl_extend("keep", {}) ]]) @@ -647,19 +661,19 @@ describe('lua stdlib', function() return vim.tbl_deep_extend("force", a, b) ]]), {a = 123 }) - eq('Error executing lua: vim/shared.lua:0: invalid "behavior": nil', + matches('invalid "behavior": nil', pcall_err(exec_lua, [[ return vim.tbl_deep_extend() ]]) ) - eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 1, expected at least 3)', + matches('wrong number of arguments %(given 1, expected at least 3%)', pcall_err(exec_lua, [[ return vim.tbl_deep_extend("keep") ]]) ) - eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 2, expected at least 3)', + matches('wrong number of arguments %(given 2, expected at least 3%)', pcall_err(exec_lua, [[ return vim.tbl_deep_extend("keep", {}) ]]) @@ -692,7 +706,7 @@ describe('lua stdlib', function() it('vim.list_extend', function() eq({1,2,3}, exec_lua [[ return vim.list_extend({1}, {2,3}) ]]) - eq('Error executing lua: vim/shared.lua:0: src: expected table, got nil', + matches('src: expected table, got nil', pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]])) eq({1,2}, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]]) eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]]) @@ -716,7 +730,7 @@ describe('lua stdlib', function() assert(vim.deep_equal(a, { A = 1; [1] = 'A'; })) vim.tbl_add_reverse_lookup(a) ]] - matches('^Error executing lua: vim/shared%.lua:0: The reverse lookup found an existing value for "[1A]" while processing key "[1A]"$', + matches('The reverse lookup found an existing value for "[1A]" while processing key "[1A]"$', pcall_err(exec_lua, code)) end) @@ -757,7 +771,7 @@ describe('lua stdlib', function() end) it('vim.fn should error when calling API function', function() - eq('Error executing lua: vim.lua:0: Tried to call API function with vim.fn: use vim.api.nvim_get_current_line instead', + matches('Tried to call API function with vim.fn: use vim.api.nvim_get_current_line instead', pcall_err(exec_lua, "vim.fn.nvim_get_current_line()")) end) @@ -893,37 +907,37 @@ describe('lua stdlib', function() exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") - eq('Error executing lua: [string "<nvim>"]:0: opt[1]: expected table, got number', + matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) - eq('Error executing lua: [string "<nvim>"]:0: invalid type name: x', + matches('invalid type name: x', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) - eq('Error executing lua: [string "<nvim>"]:0: invalid type name: 1', + matches('invalid type name: 1', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 1 }}")) - eq('Error executing lua: [string "<nvim>"]:0: invalid type name: nil', + matches('invalid type name: nil', pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}")) -- Validated parameters are required by default. - eq('Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil', + matches('arg1: expected string, got nil', pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}")) -- Explicitly required. - eq('Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil', + matches('arg1: expected string, got nil', pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")) - eq('Error executing lua: [string "<nvim>"]:0: arg1: expected table, got number', + matches('arg1: expected table, got number', pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}")) - eq('Error executing lua: [string "<nvim>"]:0: arg2: expected string, got number', + matches('arg2: expected string, got number', pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) - eq('Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil', + matches('arg2: expected string, got nil', pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) - eq('Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil', + matches('arg2: expected string, got nil', pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) - eq('Error executing lua: [string "<nvim>"]:0: arg1: expected even number, got 3', + matches('arg1: expected even number, got 3', pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) - eq('Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3', + matches('arg1: expected %?, got 3', pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) -- Pass an additional message back. - eq('Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3. Info: TEST_MSG', + matches('arg1: expected %?, got 3. Info: TEST_MSG', pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1, 'TEST_MSG' end}}")) end) @@ -968,7 +982,7 @@ describe('lua stdlib', function() ]] eq(NIL, funcs.luaeval "vim.g.to_delete") - matches([[^Error executing lua: .*: attempt to index .* nil value]], + matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.g[0].testing')) end) @@ -995,7 +1009,7 @@ describe('lua stdlib', function() return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL} ]]) - matches([[^Error executing lua: .*: attempt to index .* nil value]], + matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.b[BUF][0].testing')) eq({hello="world"}, funcs.luaeval "vim.b.to_delete") @@ -1032,7 +1046,7 @@ describe('lua stdlib', function() eq(NIL, funcs.luaeval "vim.w.nonexistant") eq(NIL, funcs.luaeval "vim.w[WIN].nonexistant") - matches([[^Error executing lua: .*: attempt to index .* nil value]], + matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.w[WIN][0].testing')) eq({hello="world"}, funcs.luaeval "vim.w.to_delete") @@ -1064,7 +1078,7 @@ describe('lua stdlib', function() eq(123, funcs.luaeval "vim.t[0].other") eq(NIL, funcs.luaeval "vim.t[0].nonexistant") - matches([[^Error executing lua: .*: attempt to index .* nil value]], + matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.t[0][0].testing')) eq({hello="world"}, funcs.luaeval "vim.t.to_delete") @@ -1094,7 +1108,7 @@ describe('lua stdlib', function() eq(funcs.luaeval "vim.api.nvim_get_vvar('progpath')", funcs.luaeval "vim.v.progpath") eq(false, funcs.luaeval "vim.v['false']") eq(NIL, funcs.luaeval "vim.v.null") - matches([[^Error executing lua: .*: attempt to index .* nil value]], + matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.v[0].progpath')) end) @@ -1114,9 +1128,9 @@ describe('lua stdlib', function() ]] eq('', funcs.luaeval "vim.bo.filetype") eq(true, funcs.luaeval "vim.bo[BUF].modifiable") - matches("^Error executing lua: .*: Invalid option name: 'nosuchopt'$", + matches("Invalid option name: 'nosuchopt'$", pcall_err(exec_lua, 'return vim.bo.nosuchopt')) - matches("^Error executing lua: .*: Expected lua string$", + matches("Expected lua string$", pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) end) @@ -1133,9 +1147,9 @@ describe('lua stdlib', function() eq(0, funcs.luaeval "vim.wo.cole") eq(0, funcs.luaeval "vim.wo[0].cole") eq(0, funcs.luaeval "vim.wo[1001].cole") - matches("^Error executing lua: .*: Invalid option name: 'notanopt'$", + matches("Invalid option name: 'notanopt'$", pcall_err(exec_lua, 'return vim.wo.notanopt')) - matches("^Error executing lua: .*: Expected lua string$", + matches("Expected lua string$", pcall_err(exec_lua, 'return vim.wo[0][0].list')) eq(2, funcs.luaeval "vim.wo[1000].cole") exec_lua [[ @@ -1226,7 +1240,7 @@ describe('lua stdlib', function() vim.opt.makeprg = "global-local" table.insert(result, vim.api.nvim_get_option('makeprg')) - table.insert(result, (pcall(vim.api.nvim_buf_get_option, 0, 'makeprg'))) + table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg')) vim.opt_local.mp = "only-local" table.insert(result, vim.api.nvim_get_option('makeprg')) @@ -1244,7 +1258,7 @@ describe('lua stdlib', function() -- Set -> global & local eq("global-local", result[1]) - eq(false, result[2]) + eq("", result[2]) -- Setlocal -> only local eq("global-local", result[3]) @@ -1254,9 +1268,9 @@ describe('lua stdlib', function() eq("only-global", result[5]) eq("only-local", result[6]) - -- set -> doesn't override previously set value + -- Set -> sets global value and resets local value eq("global-local", result[7]) - eq("only-local", result[8]) + eq("", result[8]) end) it('should allow you to retrieve window opts even if they have not been set', function() diff --git a/test/functional/options/chars_spec.lua b/test/functional/options/chars_spec.lua index 5439ca3dba..a082204980 100644 --- a/test/functional/options/chars_spec.lua +++ b/test/functional/options/chars_spec.lua @@ -67,36 +67,52 @@ describe("'fillchars'", function() shouldfail('eob:xy') -- two ascii chars shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8 end) - it('has global value', function() - screen:try_resize(50, 5) - insert("foo\nbar") - command('set laststatus=0') - command('1,2fold') - command('vsplit') - command('set fillchars=fold:x') - screen:expect([[ - ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: fooxxxxxxx| - ~ │~ | - ~ │~ | - ~ │~ | - | - ]]) - end) - it('has local window value', function() - screen:try_resize(50, 5) - insert("foo\nbar") - command('set laststatus=0') - command('1,2fold') - command('vsplit') - command('setl fillchars=fold:x') - screen:expect([[ - ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: foo·······| - ~ │~ | - ~ │~ | - ~ │~ | - | - ]]) - end) + end) + it('has global value', function() + screen:try_resize(50, 5) + insert("foo\nbar") + command('set laststatus=0') + command('1,2fold') + command('vsplit') + command('set fillchars=fold:x') + screen:expect([[ + ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: fooxxxxxxx| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) + end) + it('has window-local value', function() + screen:try_resize(50, 5) + insert("foo\nbar") + command('set laststatus=0') + command('1,2fold') + command('vsplit') + command('setl fillchars=fold:x') + screen:expect([[ + ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: foo·······| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) + end) + it('using :set clears window-local value', function() + screen:try_resize(50, 5) + insert("foo\nbar") + command('set laststatus=0') + command('setl fillchars=fold:x') + command('1,2fold') + command('vsplit') + command('set fillchars&') + screen:expect([[ + ^+-- 2 lines: foo········│+-- 2 lines: fooxxxxxxx| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) end) end) @@ -122,7 +138,7 @@ describe("'listchars'", function() | ]]) end) - it('has value local to window', function() + it('has window-local value', function() feed('i<tab><tab><tab><esc>') command('set list laststatus=0') command('setl listchars=tab:<->') @@ -136,4 +152,18 @@ describe("'listchars'", function() | ]]) end) + it('using :set clears window-local value', function() + feed('i<tab><tab><tab><esc>') + command('set list laststatus=0') + command('setl listchars=tab:<->') + command('vsplit') + command('set listchars=tab:>-,eol:$') + screen:expect([[ + >------->-------^>-------$│<------><------><------>| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) + end) end) diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index b567b3e20c..37de5d0ce6 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -156,7 +156,7 @@ describe('health.vim', function() test_plug.submodule_failed: require("test_plug.submodule_failed.health").check() ======================================================================== - ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception: - function health#check, line 24]]) + function health#check, line 20]]) eq(expected, received) end) @@ -167,7 +167,7 @@ describe('health.vim', function() broken: health#broken#check ======================================================================== - ERROR: Failed to run healthcheck for "broken" plugin. Exception: - function health#check[24]..health#broken#check, line 1 + function health#check[20]..health#broken#check, line 1 caused an error ]]) end) @@ -186,7 +186,7 @@ describe('health.vim', function() test_plug.submodule_failed: require("test_plug.submodule_failed.health").check() ======================================================================== - ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception: - function health#check, line 24]]) + function health#check, line 20]]) eq(expected, received) end) diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua index fe4f8f3593..4e3eddb960 100644 --- a/test/functional/plugin/lsp/incremental_sync_spec.lua +++ b/test/functional/plugin/lsp/incremental_sync_spec.lua @@ -66,6 +66,7 @@ local function test_edit(prev_buffer, edit_operations, expected_text_changes, of exec_lua("test_unreg = 'test1'") end + describe('incremental synchronization', function() describe('single line edit', function() it('inserting a character in an empty buffer', function() @@ -163,54 +164,314 @@ describe('incremental synchronization', function() } test_edit({"a"}, {"rb"}, expected_text_changes, 'utf-16', '\n') end) - describe('multi-byte edits', function() - it('join and undo', function() - local expected_text_changes = { - { - range = { - ['start'] = { - character = 11, - line = 0 - }, - ['end'] = { - character = 11, - line = 0 - } + it('deleting a line', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 0 }, - rangeLength = 0, - text = ' test3' - },{ - range = { - ['start'] = { - character = 0, - line = 1 - }, - ['end'] = { - character = 0, - line = 2 - } + ['end'] = { + character = 0, + line = 1 + } + }, + rangeLength = 12, + text = '' + } + } + test_edit({"hello world"}, {"dd"}, expected_text_changes, 'utf-16', '\n') + end) + it('deleting an empty line', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 1 }, - rangeLength = 6, - text = '' - },{ - range = { - ['start'] = { - character = 11, - line = 0 - }, - ['end'] = { - character = 17, - line = 0 - } + ['end'] = { + character = 0, + line = 2 + } + }, + rangeLength = 1, + text = '' + } + } + test_edit({"hello world", ""}, {"jdd"}, expected_text_changes, 'utf-16', '\n') + end) + it('adding a line', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 1 + }, + ['end'] = { + character = 0, + line = 1 + } + }, + rangeLength = 0, + text = 'hello world\n' + } + } + test_edit({"hello world"}, {"yyp"}, expected_text_changes, 'utf-16', '\n') + end) + it('adding an empty line', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 1 }, - rangeLength = 6, - text = '\ntest3' + ['end'] = { + character = 0, + line = 1 + } }, + rangeLength = 0, + text = '\n' } - test_edit({"test1 test2", "test3"}, {"J", "u"}, expected_text_changes, 'utf-16', '\n') - end) + } + test_edit({"hello world"}, {"o"}, expected_text_changes, 'utf-16', '\n') end) end) + describe('multi line edit', function() + it('deletion and insertion', function() + local expected_text_changes = { + -- delete "_fsda" from end of line 1 + { + range = { + ['start'] = { + character = 4, + line = 1 + }, + ['end'] = { + character = 9, + line = 1 + } + }, + rangeLength = 5, + text = '' + }, + -- delete "hello world\n" from line 2 + { + range = { + ['start'] = { + character = 0, + line = 2 + }, + ['end'] = { + character = 0, + line = 3 + } + }, + rangeLength = 12, + text = '' + }, + -- delete "1234" from beginning of line 2 + { + range = { + ['start'] = { + character = 0, + line = 2 + }, + ['end'] = { + character = 4, + line = 2 + } + }, + rangeLength = 4, + text = '' + }, + -- add " asdf" to end of line 1 + { + range = { + ['start'] = { + character = 4, + line = 1 + }, + ['end'] = { + character = 4, + line = 1 + } + }, + rangeLength = 0, + text = ' asdf' + }, + -- delete " asdf\n" from line 2 + { + range = { + ['start'] = { + character = 0, + line = 2 + }, + ['end'] = { + character = 0, + line = 3 + } + }, + rangeLength = 6, + text = '' + }, + -- undo entire deletion + { + range = { + ['start'] = { + character = 4, + line = 1 + }, + ['end'] = { + character = 9, + line = 1 + } + }, + rangeLength = 5, + text = "_fdsa\nhello world\n1234 asdf" + }, + -- redo entire deletion + { + range = { + ['start'] = { + character = 4, + line = 1 + }, + ['end'] = { + character = 9, + line = 3 + } + }, + rangeLength = 27, + text = ' asdf' + }, + } + local original_lines = { + "\\begin{document}", + "test_fdsa", + "hello world", + "1234 asdf", + "\\end{document}" + } + test_edit(original_lines, {"jf_vejjbhhdu<C-R>"}, expected_text_changes, 'utf-16', '\n') + end) + end) + + describe('multi-operation edits', function() + it('mult-line substitution', function() + local expected_text_changes = { + { + range = { + ["end"] = { + character = 11, + line = 2 }, + ["start"] = { + character = 10, + line = 2 } }, + rangeLength = 1, + text = '', + },{ + range = { + ["end"] = { + character = 10, + line = 2 }, + start = { + character = 10, + line = 2 } }, + rangeLength = 0, + text = '2', + },{ + range = { + ["end"] = { + character = 11, + line = 3 }, + ["start"] = { + character = 10, + line = 3 } }, + rangeLength = 1, + text = '' + },{ + range = { + ['end'] = { + character = 10, + line = 3 }, + ['start'] = { + character = 10, + line = 3 } }, + rangeLength = 0, + text = '3' }, + { + range = { + ['end'] = { + character = 0, + line = 3 }, + ['start'] = { + character = 12, + line = 2 } }, + rangeLength = 1, + text = '\n' + } + } + local original_lines = { + "\\begin{document}", + "\\section*{1}", + "\\section*{1}", + "\\section*{1}", + "\\end{document}" + } + test_edit(original_lines, {"3gg$h<C-V>jg<C-A>"}, expected_text_changes, 'utf-16', '\n') + end) + it('join and undo', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 11, + line = 0 + }, + ['end'] = { + character = 11, + line = 0 + } + }, + rangeLength = 0, + text = ' test3' + },{ + range = { + ['start'] = { + character = 0, + line = 1 + }, + ['end'] = { + character = 0, + line = 2 + } + }, + rangeLength = 6, + text = '' + },{ + range = { + ['start'] = { + character = 11, + line = 0 + }, + ['end'] = { + character = 17, + line = 0 + } + }, + rangeLength = 6, + text = '\ntest3' + }, + } + test_edit({"test1 test2", "test3"}, {"J", "u"}, expected_text_changes, 'utf-16', '\n') + end) + end) + describe('multi-byte edits', function() it('deleting a multibyte character', function() local expected_text_changes = { @@ -231,7 +492,105 @@ describe('incremental synchronization', function() } test_edit({"🔥"}, {"x"}, expected_text_changes, 'utf-16', '\n') end) - it('deleting a multiple lines containing multibyte characters', function() + it('replacing a multibyte character with matching prefix', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 1 + }, + ['end'] = { + character = 1, + line = 1 + } + }, + rangeLength = 1, + text = '⟩' + } + } + -- ⟨ is e29fa8, ⟩ is e29fa9 + local original_lines = { + "\\begin{document}", + "⟨", + "\\end{document}", + } + test_edit(original_lines, {"jr⟩"}, expected_text_changes, 'utf-16', '\n') + end) + it('replacing a multibyte character with matching suffix', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 1 + }, + ['end'] = { + character = 1, + line = 1 + } + }, + rangeLength = 1, + text = 'ḟ' + } + } + -- ฟ is e0b89f, ḟ is e1b89f + local original_lines = { + "\\begin{document}", + "ฟ", + "\\end{document}", + } + test_edit(original_lines, {"jrḟ"}, expected_text_changes, 'utf-16', '\n') + end) + it('inserting before a multibyte character', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 1 + }, + ['end'] = { + character = 0, + line = 1 + } + }, + rangeLength = 0, + text = ' ' + } + } + local original_lines = { + "\\begin{document}", + "→", + "\\end{document}", + } + test_edit(original_lines, {"ji "}, expected_text_changes, 'utf-16', '\n') + end) + it('deleting a multibyte character from a long line', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 85, + line = 1 + }, + ['end'] = { + character = 86, + line = 1 + } + }, + rangeLength = 1, + text = '' + } + } + local original_lines = { + "\\begin{document}", + "→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→", + "\\end{document}", + } + test_edit(original_lines, {"jx"}, expected_text_changes, 'utf-16', '\n') + end) + it('deleting multiple lines containing multibyte characters', function() local expected_text_changes = { { range = { diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 228fc06e9b..b12d4227d5 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -66,7 +66,10 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options) end end; }); - root_dir = vim.loop.cwd(); + workspace_folders = {{ + uri = 'file://' .. vim.loop.cwd(), + name = 'test_folder', + }}; on_init = function(client, result) TEST_RPC_CLIENT = client vim.rpcrequest(1, "init", result) @@ -153,7 +156,10 @@ describe('LSP', function() "-c", string.format("lua TEST_NAME = %q", test_name), "-c", "luafile "..fixture_filename; }; - root_dir = vim.loop.cwd(); + workspace_folders = {{ + uri = 'file://' .. vim.loop.cwd(), + name = 'test_folder', + }}; } end TEST_CLIENT1 = test__start_client() diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 7dcca231ee..f25cfa2039 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -6,9 +6,11 @@ local poke_eventloop = helpers.poke_eventloop local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file -local command= helpers.command +local command = helpers.command local exc_exec = helpers.exc_exec local matches = helpers.matches +local exec_lua = helpers.exec_lua +local sleep = helpers.sleep describe(':terminal buffer', function() local screen @@ -328,3 +330,37 @@ describe('No heap-buffer-overflow when', function() assert_alive() end) end) + +describe('on_lines does not emit out-of-bounds line indexes when', function() + before_each(function() + clear() + exec_lua([[ + function _G.register_callback(bufnr) + _G.cb_error = '' + vim.api.nvim_buf_attach(bufnr, false, { + on_lines = function(_, bufnr, _, firstline, _, _) + local status, msg = pcall(vim.api.nvim_buf_get_offset, bufnr, firstline) + if not status then + _G.cb_error = msg + end + end + }) + end + ]]) + end) + + it('creating a terminal buffer #16394', function() + feed_command([[autocmd TermOpen * ++once call v:lua.register_callback(expand("<abuf>"))]]) + feed_command('terminal') + sleep(500) + eq('', exec_lua([[return _G.cb_error]])) + end) + + it('deleting a terminal buffer #16394', function() + feed_command('terminal') + sleep(500) + feed_command('lua _G.register_callback(0)') + feed_command('bdelete!') + eq('', exec_lua([[return _G.cb_error]])) + end) +end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 8d70ebf679..e9495f45a2 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -3,6 +3,8 @@ local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local nvim_dir, command = helpers.nvim_dir, helpers.command +local nvim_prog = helpers.nvim_prog +local eq, eval = helpers.eq, helpers.eval local feed_command = helpers.feed_command local hide_cursor = thelpers.hide_cursor local show_cursor = thelpers.show_cursor @@ -173,3 +175,705 @@ describe('cursor with customized highlighting', function() end) end) +describe('buffer cursor position is correct in terminal without number column', function() + local screen + + local function setup_ex_register(str) + screen = thelpers.screen_setup(0, '["'..nvim_prog + ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + -- <Left> and <Right> don't always work + ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :{1: } | + {3:-- TERMINAL --} | + ]]) + end + + before_each(clear) + + describe('in a line with no multibyte characters or trailing spaces,', function() + before_each(function() + setup_ex_register('aaaaaaaa') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaaaaa{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 9}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaaaa^a{2: } | + | + ]]) + eq({6, 8}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaaa{1:a}a | + {3:-- TERMINAL --} | + ]]) + eq({6, 7}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaa^a{2:a}a | + | + ]]) + eq({6, 6}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :a{1:a}aaaaaa | + {3:-- TERMINAL --} | + ]]) + eq({6, 2}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^a{2:a}aaaaaa | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell multibyte characters and no trailing spaces,', function() + before_each(function() + setup_ex_register('µµµµµµµµ') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµµµµ{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 17}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµµµ^µ{2: } | + | + ]]) + eq({6, 15}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµµ{1:µ}µ | + {3:-- TERMINAL --} | + ]]) + eq({6, 13}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµ^µ{2:µ}µ | + | + ]]) + eq({6, 11}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ{1:µ}µµµµµµ | + {3:-- TERMINAL --} | + ]]) + eq({6, 3}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^µ{2:µ}µµµµµµ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell composed multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 33}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{2: } | + | + ]]) + eq({6, 29}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳µ̳{1:µ̳}µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳^µ̳{2:µ̳}µ̳ | + | + ]]) + eq({6, 21}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳{1:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 5}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^µ̳{2:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with double-cell multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('哦哦哦哦哦哦哦哦') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦哦哦哦{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦哦哦^哦{2: } | + | + ]]) + eq({6, 22}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦哦{1:哦}哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 19}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦^哦{2:哦}哦 | + | + ]]) + eq({6, 16}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦{1:哦}哦哦哦哦哦哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 4}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^哦{2:哦}哦哦哦哦哦哦 | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) +end) + +describe('buffer cursor position is correct in terminal with number column', function() + local screen + + local function setup_ex_register(str) + screen = thelpers.screen_setup(0, '["'..nvim_prog + ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + -- <Left> and <Right> don't always work + ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:{1: } | + {3:-- TERMINAL --} | + ]]) + end + + before_each(function() + clear() + command('set number') + end) + + describe('in a line with no multibyte characters or trailing spaces,', function() + before_each(function() + setup_ex_register('aaaaaaaa') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaaaaa{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 9}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaaaa^a{2: } | + | + ]]) + eq({6, 8}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaaa{1:a}a | + {3:-- TERMINAL --} | + ]]) + eq({6, 7}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaa^a{2:a}a | + | + ]]) + eq({6, 6}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:a{1:a}aaaaaa | + {3:-- TERMINAL --} | + ]]) + eq({6, 2}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^a{2:a}aaaaaa | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell multibyte characters and no trailing spaces,', function() + before_each(function() + setup_ex_register('µµµµµµµµ') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµµµµ{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 17}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµµµ^µ{2: } | + | + ]]) + eq({6, 15}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµµ{1:µ}µ | + {3:-- TERMINAL --} | + ]]) + eq({6, 13}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµ^µ{2:µ}µ | + | + ]]) + eq({6, 11}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ{1:µ}µµµµµµ | + {3:-- TERMINAL --} | + ]]) + eq({6, 3}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^µ{2:µ}µµµµµµ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell composed multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 33}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{2: } | + | + ]]) + eq({6, 29}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳{1:µ̳}µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳^µ̳{2:µ̳}µ̳ | + | + ]]) + eq({6, 21}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳{1:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 5}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^µ̳{2:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with double-cell multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('哦哦哦哦哦哦哦哦') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦哦哦哦{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦哦哦^哦{2: } | + | + ]]) + eq({6, 22}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦哦{1:哦}哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 19}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦^哦{2:哦}哦 | + | + ]]) + eq({6, 16}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦{1:哦}哦哦哦哦哦哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 4}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^哦{2:哦}哦哦哦哦哦哦 | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) +end) diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index b932c58430..11bdc73a47 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -12,6 +12,8 @@ local curbufmeths = helpers.curbufmeths local nvim = helpers.nvim local feed_data = thelpers.feed_data local pcall_err = helpers.pcall_err +local exec_lua = helpers.exec_lua +local assert_alive = helpers.assert_alive describe(':terminal scrollback', function() local screen @@ -527,3 +529,71 @@ describe("'scrollback' option", function() end) end) + +describe("pending scrollback line handling", function() + local screen + + before_each(function() + clear() + screen = Screen.new(30, 7) + screen:attach() + screen:set_default_attr_ids { + [1] = {foreground = Screen.colors.Brown}, + [2] = {reverse = true}, + [3] = {bold = true}, + } + end) + + it("does not crash after setting 'number' #14891", function() + exec_lua [[ + local a = vim.api + local buf = a.nvim_create_buf(true, true) + local chan = a.nvim_open_term(buf, {}) + a.nvim_win_set_option(0, "number", true) + a.nvim_chan_send(chan, ("a\n"):rep(11) .. "a") + a.nvim_win_set_buf(0, buf) + ]] + screen:expect [[ + {1: 1 }^a | + {1: 2 } a | + {1: 3 } a | + {1: 4 } a | + {1: 5 } a | + {1: 6 } a | + | + ]] + feed('G') + screen:expect [[ + {1: 7 } a | + {1: 8 } a | + {1: 9 } a | + {1: 10 } a | + {1: 11 } a | + {1: 12 } ^a | + | + ]] + assert_alive() + end) + + it("does not crash after nvim_buf_call #14891", function() + exec_lua [[ + local a = vim.api + local bufnr = a.nvim_create_buf(false, true) + a.nvim_buf_call(bufnr, function() + vim.fn.termopen({"echo", ("hi\n"):rep(11)}) + end) + a.nvim_win_set_buf(0, bufnr) + vim.cmd("startinsert") + ]] + screen:expect [[ + hi | + hi | + hi | + | + | + [Process exited 0]{2: } | + {3:-- TERMINAL --} | + ]] + assert_alive() + end) +end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 6b9586b4de..bf57b135cb 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -20,6 +20,7 @@ local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set local ok = helpers.ok local read_file = helpers.read_file +local exec_lua = helpers.exec_lua if helpers.pending_win32(pending) then return end @@ -580,21 +581,34 @@ describe('TUI', function() end) it("paste: 'nomodifiable' buffer", function() + local has_luajit = exec_lua('return jit ~= nil') child_session:request('nvim_command', 'set nomodifiable') child_session:request('nvim_exec_lua', [[ -- Stack traces for this test are non-deterministic, so disable them _G.debug.traceback = function(msg) return msg end ]], {}) feed_data('\027[200~fail 1\nfail 2\n\027[201~') - screen:expect{grid=[[ - | - {4:~ }| - {5: }| - {8:paste: Error executing lua: vim.lua:243: Vim:E21: }| - {8:Cannot make changes, 'modifiable' is off} | - {10:Press ENTER or type command to continue}{1: } | - {3:-- TERMINAL --} | - ]]} + if has_luajit then + screen:expect{grid=[[ + | + {4:~ }| + {5: }| + {8:paste: Error executing lua: vim.lua:0: Vim:E21: Ca}| + {8:nnot make changes, 'modifiable' is off} | + {10:Press ENTER or type command to continue}{1: } | + {3:-- TERMINAL --} | + ]]} + else + screen:expect{grid=[[ + | + {4:~ }| + {5: }| + {8:paste: Error executing lua: Vim:E21: Cannot make c}| + {8:hanges, 'modifiable' is off} | + {10:Press ENTER or type command to continue}{1: } | + {3:-- TERMINAL --} | + ]]} + end feed_data('\n') -- <Enter> child_session:request('nvim_command', 'set modifiable') feed_data('\027[200~success 1\nsuccess 2\n\027[201~') @@ -677,8 +691,8 @@ describe('TUI', function() item 2997 | item 2998 | item 2999 | - item 3000 en{1:d} | - {5:[No Name] [+] 3000,13 Bot}| + item 3000 en{1:d}d | + {5:[No Name] [+] 5999,13 Bot}| | {3:-- TERMINAL --} | ]]) @@ -765,6 +779,44 @@ describe('TUI', function() ]]) end) + it('paste: streamed paste with isolated "stop paste" code', function() + child_session:request('nvim_exec_lua', [[ + _G.paste_phases = {} + vim.paste = (function(overridden) + return function(lines, phase) + table.insert(_G.paste_phases, phase) + overridden(lines, phase) + end + end)(vim.paste) + ]], {}) + feed_data('i') + feed_data('\027[200~pasted') -- phase 1 + screen:expect([[ + pasted{1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) + feed_data(' from terminal') -- phase 2 + screen:expect([[ + pasted from terminal{1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) + -- Send isolated "stop paste" sequence. + feed_data('\027[201~') -- phase 3 + screen:expect_unchanged() + local _, rv = child_session:request('nvim_exec_lua', [[return _G.paste_phases]], {}) + eq({1, 2, 3}, rv) + end) + it('allows termguicolors to be set at runtime', function() screen:set_option('rgb', true) screen:set_default_attr_ids({ diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index ffaa4141c4..911fa017ab 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -227,15 +227,52 @@ void ui_refresh(void) }, res) end) + it('supports getting text of multiline node', function() + if pending_c_parser(pending) then return end + insert(test_text) + local res = exec_lua([[ + local parser = vim.treesitter.get_parser(0, "c") + local tree = parser:parse()[1] + return vim.treesitter.get_node_text(tree:root(), 0) + ]]) + eq(test_text, res) + + local res2 = exec_lua([[ + local parser = vim.treesitter.get_parser(0, "c") + local root = parser:parse()[1]:root() + return vim.treesitter.get_node_text(root:child(0):child(0), 0) + ]]) + eq('void', res2) + end) + + it('support getting text where start of node is past EOF', function() + local text = [[ +def run + a = <<~E +end]] + insert(text) + local result = exec_lua([[ + local fake_node = {} + function fake_node:start() + return 3, 0, 23 + end + function fake_node:end_() + return 3, 0, 23 + end + return vim.treesitter.get_node_text(fake_node, 0) == nil + ]]) + eq(true, result) + end) + it('can match special regex characters like \\ * + ( with `vim-match?`', function() insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') local res = exec_lua([[ - cquery = vim.treesitter.parse_query("c", '((_) @plus (vim-match? @plus "^\\\\+$"))'.. - '((_) @times (vim-match? @times "^\\\\*$"))'.. - '((_) @paren (vim-match? @paren "^\\\\($"))'.. - '((_) @escape (vim-match? @escape "^\\\\\\\\n$"))'.. - '((_) @string (vim-match? @string "^\\"\\\\\\\\n\\"$"))') + cquery = vim.treesitter.parse_query("c", '([_] @plus (#vim-match? @plus "^\\\\+$"))'.. + '([_] @times (#vim-match? @times "^\\\\*$"))'.. + '([_] @paren (#vim-match? @paren "^\\\\($"))'.. + '([_] @escape (#vim-match? @escape "^\\\\\\\\n$"))'.. + '([_] @string (#vim-match? @string "^\\"\\\\\\\\n\\"$"))') parser = vim.treesitter.get_parser(0, "c") tree = parser:parse()[1] res = {} @@ -321,7 +358,7 @@ void ui_refresh(void) insert('char* astring = "Hello World!";') local res = exec_lua([[ - cquery = vim.treesitter.parse_query("c", '((_) @quote (vim-match? @quote "^\\"$")) ((_) @quote (lua-match? @quote "^\\"$"))') + cquery = vim.treesitter.parse_query("c", '([_] @quote (#vim-match? @quote "^\\"$")) ([_] @quote (#lua-match? @quote "^\\"$"))') parser = vim.treesitter.get_parser(0, "c") tree = parser:parse()[1] res = {} diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 9c035c728b..03cd4bfd06 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -212,10 +212,10 @@ describe('ui/cursor', function() if m.blinkwait then m.blinkwait = 700 end end if m.hl_id then - m.hl_id = 58 + m.hl_id = 60 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 59 end + if m.id_lm then m.id_lm = 61 end end -- Assert the new expectation. diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index dce6384b9b..1575cab591 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1031,6 +1031,69 @@ if (h->n_buckets < new_n_buckets) { // expand | ]]} + screen:try_resize(50, 11) + feed('gg') + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + feed('G<C-E>') + screen:expect{grid=[[ + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + Grugg | + | + ]]} + + feed('gg') + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + screen:try_resize(50, 12) + feed('G') + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + Grugg | + | + ]]} + meths.buf_del_extmark(0, ns, id) screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index 13949b0756..bd2692d19a 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -186,6 +186,19 @@ describe('Diff mode screen', function() {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) + + screen:try_resize(40, 9) + screen:expect([[ + {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}| + {1: }5 {3:│}{1: }5 | + {1: }6 {3:│}{1: }6 | + {1: }7 {3:│}{1: }7 | + {1: }8 {3:│}{1: }8 | + {1: }9 {3:│}{1: }9 | + {1: }10 {3:│}{1: }10 | + {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + | + ]]) end) it('Add a line at the end of file 1', function() @@ -232,6 +245,19 @@ describe('Diff mode screen', function() {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) + + screen:try_resize(40, 9) + screen:expect([[ + {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}| + {1: }5 {3:│}{1: }5 | + {1: }6 {3:│}{1: }6 | + {1: }7 {3:│}{1: }7 | + {1: }8 {3:│}{1: }8 | + {1: }9 {3:│}{1: }9 | + {1: }10 {3:│}{1: }10 | + {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + | + ]]) end) it('Add a line in the middle of file 2, remove on at the end of file 1', function() diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 249686234c..3e0e15c2b7 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -41,6 +41,7 @@ describe("folded lines", function() [9] = {bold = true, foreground = Screen.colors.Brown}, [10] = {background = Screen.colors.LightGrey, underline = true}, [11] = {bold=true}, + [12] = {background = Screen.colors.Grey90}, }) end) @@ -84,6 +85,117 @@ describe("folded lines", function() end end) + it("highlights with CursorLineFold when 'cursorline' is set", function() + command("set cursorline foldcolumn=2 foldmethod=marker") + command("hi link CursorLineFold Search") + insert(content1) + feed("zf3j") + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {7: }This is a | + {7: }valid English | + {7: }sentence composed by | + {7: }an exhausted developer | + {7: }in his cave. | + {6: }{12:^ }| + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {7: }This is a | + {7: }valid English | + {7: }sentence composed by | + {7: }an exhausted developer | + {7: }in his cave. | + {6: }{12:^ }| + {1:~ }| + | + ]]) + end + feed("k") + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {7: }This is a | + {7: }valid English | + {7: }sentence composed by | + {7: }an exhausted developer | + {6: }{12:^in his cave. }| + {7: } | + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {7: }This is a | + {7: }valid English | + {7: }sentence composed by | + {7: }an exhausted developer | + {6: }{12:^in his cave. }| + {7: } | + {1:~ }| + | + ]]) + end + command("set cursorlineopt=line") + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {7: }This is a | + {7: }valid English | + {7: }sentence composed by | + {7: }an exhausted developer | + {7: }{12:^in his cave. }| + {7: } | + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {7: }This is a | + {7: }valid English | + {7: }sentence composed by | + {7: }an exhausted developer | + {7: }{12:^in his cave. }| + {7: } | + {1:~ }| + | + ]]) + end + end) + it("highlighting with relative line numbers", function() command("set relativenumber cursorline cursorlineopt=number foldmethod=marker") feed_command("set foldcolumn=2") diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index c00d30fe32..0983d0d4ad 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -276,6 +276,24 @@ describe('highlight defaults', function() ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) end) + it('linking updates window highlight immediately #16552', function() + screen:try_resize(53, 4) + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + | + ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) + feed_command("hi NonTextAlt guifg=Red") + feed_command("hi! link NonText NonTextAlt") + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + :hi! link NonText NonTextAlt | + ]], {[0] = {foreground=Screen.colors.Red}}) + end) + it('Cursor after `:hi clear|syntax reset` #6508', function() command('highlight clear|syntax reset') eq('guifg=bg guibg=fg', eval([[matchstr(execute('hi Cursor'), '\v(gui|cterm).*$')]])) diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 741b93043d..dcd31cfdb7 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -157,6 +157,99 @@ describe('Signs', function() ]]) end) + it('higlights the cursorline sign with culhl', function() + feed('ia<cr>b<cr>c<esc>') + command('sign define piet text=>> texthl=Search culhl=ErrorMsg') + command('sign place 1 line=1 name=piet buffer=1') + command('sign place 2 line=2 name=piet buffer=1') + command('sign place 3 line=3 name=piet buffer=1') + command('set cursorline') + screen:expect([[ + {1:>>}a | + {1:>>}b | + {8:>>}{3:^c }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed('k') + screen:expect([[ + {1:>>}a | + {8:>>}{3:^b }| + {1:>>}c | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + command('set nocursorline') + screen:expect([[ + {1:>>}a | + {1:>>}^b | + {1:>>}c | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + command('set cursorline cursorlineopt=line') + screen:expect([[ + {1:>>}a | + {1:>>}{3:^b }| + {1:>>}c | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + command('set cursorlineopt=number') + screen:expect([[ + {1:>>}a | + {8:>>}^b | + {1:>>}c | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + it('multiple signs #9295', function() feed('ia<cr>b<cr>c<cr><esc>') command('set number') diff --git a/test/functional/vimscript/screenpos_spec.lua b/test/functional/vimscript/screenpos_spec.lua new file mode 100644 index 0000000000..75e5c02298 --- /dev/null +++ b/test/functional/vimscript/screenpos_spec.lua @@ -0,0 +1,51 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, eq, meths = helpers.clear, helpers.eq, helpers.meths +local command, funcs = helpers.command, helpers.funcs + +before_each(clear) + +describe('screenpos() function', function() + it('works in floating window with border', function() + local bufnr = meths.create_buf(false, true) + local opts = { + relative='editor', + height=8, + width=12, + row=6, + col=8, + anchor='NW', + style='minimal', + border='none', + focusable=1 + } + local float = meths.open_win(bufnr, false, opts) + command('redraw') + local pos = funcs.screenpos(bufnr, 1, 1) + eq(7, pos.row) + eq(9, pos.col) + + -- only left border + opts.border = {'', '', '', '', '', '', '', '|'} + meths.win_set_config(float, opts) + command('redraw') + pos = funcs.screenpos(bufnr, 1, 1) + eq(7, pos.row) + eq(10, pos.col) + + -- only top border + opts.border = {'', '_', '', '', '', '', '', ''} + meths.win_set_config(float, opts) + command('redraw') + pos = funcs.screenpos(bufnr, 1, 1) + eq(8, pos.row) + eq(9, pos.col) + + -- both left and top border + opts.border = 'single' + meths.win_set_config(float, opts) + command('redraw') + pos = funcs.screenpos(bufnr, 1, 1) + eq(8, pos.row) + eq(10, pos.col) + end) +end) diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua index 9ee0735e40..e45b64422f 100644 --- a/test/functional/vimscript/timer_spec.lua +++ b/test/functional/vimscript/timer_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local feed, eq, eval, ok = helpers.feed, helpers.eq, helpers.eval, helpers.ok local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run local clear, command, funcs = helpers.clear, helpers.command, helpers.funcs +local exc_exec = helpers.exc_exec local curbufmeths = helpers.curbufmeths local load_adjust = helpers.load_adjust local retry = helpers.retry @@ -262,4 +263,13 @@ describe('timers', function() eq(2, eval('g:val')) end) + + it("timer_start can't be used in the sandbox", function() + source [[ + function! Scary(timer) abort + call execute('echo ''execute() should be disallowed''', '') + endfunction + ]] + eq("Vim(call):E48: Not allowed in sandbox", exc_exec("sandbox call timer_start(0, 'Scary')")) + end) end) diff --git a/test/helpers.lua b/test/helpers.lua index 09b113c01d..87431e4342 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -741,9 +741,20 @@ function module.read_file_list(filename, start) if not file then return nil end + + -- There is no need to read more than the last 2MB of the log file, so seek + -- to that. + local file_size = file:seek("end") + local offset = file_size - 2000000 + if offset < 0 then + offset = 0 + end + file:seek("set", offset) + local lines = {} local i = 1 - for line in file:lines() do + local line = file:read("*l") + while line ~= nil do if i >= start then table.insert(lines, line) if #lines > maxlines then @@ -751,6 +762,7 @@ function module.read_file_list(filename, start) end end i = i + 1 + line = file:read("*l") end file:close() return lines diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index a73fc8e47e..29a2b78491 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -4,7 +4,6 @@ local cimported = helpers.cimport( './src/nvim/os/shell.h', './src/nvim/option_defs.h', './src/nvim/main.h', - './src/nvim/misc1.h', './src/nvim/memory.h' ) local ffi, eq = helpers.ffi, helpers.eq diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 41954de9be..15ce59747e 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -54,15 +54,21 @@ describe('path.c', function() eq(lfs.currentdir(), (ffi.string(buffer))) end) - itp('fails if the given directory does not exist', function() - eq(FAIL, path_full_dir_name('does_not_exist', buffer, length)) - end) - itp('works with a normal relative dir', function() local result = path_full_dir_name('unit-test-directory', buffer, length) eq(lfs.currentdir() .. '/unit-test-directory', (ffi.string(buffer))) eq(OK, result) end) + + itp('works with a non-existing relative dir', function() + local result = path_full_dir_name('does-not-exist', buffer, length) + eq(lfs.currentdir() .. '/does-not-exist', (ffi.string(buffer))) + eq(OK, result) + end) + + itp('fails with a non-existing absolute dir', function() + eq(FAIL, path_full_dir_name('/does_not_exist', buffer, length)) + end) end) describe('path_full_compare', function() diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 8ad71cfd3a..c5adf36908 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -97,7 +97,7 @@ endif() if(MINGW AND CMAKE_GENERATOR MATCHES "Ninja") find_program(MAKE_PRG NAMES mingw32-make) if(NOT MAKE_PRG) - message(FATAL_ERROR "GNU Make for mingw32 is required to build the dependecies.") + message(FATAL_ERROR "GNU Make for mingw32 is required to build the dependencies.") else() message(STATUS "Found GNU Make for mingw32: ${MAKE_PRG}") endif() @@ -157,8 +157,8 @@ set(LUAJIT_SHA256 6c9e46877db2df16ca0fa76db4043ed30a1ae60c89d9ba2c3e4d35eb2360cd set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz) set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333) -set(LUAROCKS_URL https://github.com/luarocks/luarocks/archive/v3.7.0.tar.gz) -set(LUAROCKS_SHA256 968c98ae894cea2c850f077133e3feb9f8ce94df7a33a5611bd4d25e07c94925) +set(LUAROCKS_URL https://github.com/luarocks/luarocks/archive/v3.8.0.tar.gz) +set(LUAROCKS_SHA256 ab6612ca9ab87c6984871d2712d05525775e8b50172701a0a1cabddf76de2be7) set(UNIBILIUM_URL https://github.com/neovim/unibilium/archive/92d929f.tar.gz) set(UNIBILIUM_SHA256 29815283c654277ef77a3adcc8840db79ddbb20a0f0b0c8f648bd8cd49a02e4b) @@ -200,11 +200,11 @@ set(GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47 set(LIBICONV_URL https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz) set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178) -set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/5aa0bbb.tar.gz) -set(TREESITTER_C_SHA256 a5dcb37460d83002dfae7f9a208170ddbc9a047f231b9d6b75da7d36d707db2f) +set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.1.tar.gz) +set(TREESITTER_C_SHA256 ffcc2ef0eded59ad1acec9aec4f9b0c7dd209fc1a85d85f8b0e81298e3dddcc2) -set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/7890a29db0b186b7b21a0a95d99fa6c562b8316b.tar.gz) -set(TREESITTER_SHA256 634006b0336a5eef1b07d2f80a4d4f8ac1522bf15759ec3e5dda0032a734fb19) +set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/25f64e1eb66bb1ab3eccd4f0b7da543005f3ba79.tar.gz) +set(TREESITTER_SHA256 4f43bad474df494d00a779157f1c437638987d0e7896f9c73492cfeef35366b5) if(USE_BUNDLED_UNIBILIUM) include(BuildUnibilium) diff --git a/third-party/cmake/BuildLuajit.cmake b/third-party/cmake/BuildLuajit.cmake index c4e5112dce..e02d7fe609 100644 --- a/third-party/cmake/BuildLuajit.cmake +++ b/third-party/cmake/BuildLuajit.cmake @@ -124,7 +124,9 @@ elseif(MINGW) COMMAND ${CMAKE_COMMAND} -E copy ${DEPS_BUILD_DIR}/src/luajit/src/libluajit.a ${DEPS_INSTALL_DIR}/lib COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/include/luajit-2.1 COMMAND ${CMAKE_COMMAND} -DFROM_GLOB=${DEPS_BUILD_DIR}/src/luajit/src/*.h -DTO=${DEPS_INSTALL_DIR}/include/luajit-2.1 -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyFilesGlob.cmake - ) + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/bin/lua/jit + COMMAND ${CMAKE_COMMAND} -E copy_directory ${DEPS_BUILD_DIR}/src/luajit/src/jit ${DEPS_INSTALL_DIR}/bin/lua/jit + ) elseif(MSVC) BuildLuaJit( @@ -138,8 +140,10 @@ elseif(MSVC) # Luv searches for luajit.lib COMMAND ${CMAKE_COMMAND} -E copy ${DEPS_BUILD_DIR}/src/luajit/src/lua51.lib ${DEPS_INSTALL_DIR}/lib/luajit.lib COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/include/luajit-2.1 - COMMAND ${CMAKE_COMMAND} -DFROM_GLOB=${DEPS_BUILD_DIR}/src/luajit/src/*.h -DTO=${DEPS_INSTALL_DIR}/include/luajit-2.1 -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyFilesGlob.cmake) - + COMMAND ${CMAKE_COMMAND} -DFROM_GLOB=${DEPS_BUILD_DIR}/src/luajit/src/*.h -DTO=${DEPS_INSTALL_DIR}/include/luajit-2.1 -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyFilesGlob.cmake + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/bin/lua/jit + COMMAND ${CMAKE_COMMAND} -E copy_directory ${DEPS_BUILD_DIR}/src/luajit/src/jit ${DEPS_INSTALL_DIR}/bin/lua/jit + ) else() message(FATAL_ERROR "Trying to build luajit in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}") endif() |