aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS3
-rw-r--r--.github/workflows/api-docs.yml4
-rw-r--r--.github/workflows/commitlint.yml6
-rwxr-xr-x.github/workflows/env.sh3
-rw-r--r--.github/workflows/labeler.yml16
-rw-r--r--.github/workflows/release.yml1
-rw-r--r--.github/workflows/vim-patches.yml2
-rw-r--r--CMakeLists.txt13
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--ci/build.ps111
-rwxr-xr-xci/common/submit_coverage.sh4
-rwxr-xr-xci/install.sh8
-rw-r--r--runtime/autoload/dist/ft.vim60
-rw-r--r--runtime/autoload/man.vim109
-rw-r--r--runtime/doc/api.txt10
-rw-r--r--runtime/doc/autocmd.txt34
-rw-r--r--runtime/doc/cmdline.txt5
-rw-r--r--runtime/doc/diagnostic.txt5
-rw-r--r--runtime/doc/diff.txt7
-rw-r--r--runtime/doc/editing.txt3
-rw-r--r--runtime/doc/eval.txt16
-rw-r--r--runtime/doc/filetype.txt7
-rw-r--r--runtime/doc/fold.txt4
-rw-r--r--runtime/doc/helphelp.txt4
-rw-r--r--runtime/doc/if_cscop.txt2
-rw-r--r--runtime/doc/indent.txt4
-rw-r--r--runtime/doc/insert.txt4
-rw-r--r--runtime/doc/intro.txt1
-rw-r--r--runtime/doc/lsp.txt45
-rw-r--r--runtime/doc/lua.txt9
-rw-r--r--runtime/doc/map.txt12
-rw-r--r--runtime/doc/mbyte.txt4
-rw-r--r--runtime/doc/message.txt12
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt5
-rw-r--r--runtime/doc/options.txt27
-rw-r--r--runtime/doc/pattern.txt6
-rw-r--r--runtime/doc/quickfix.txt2
-rw-r--r--runtime/doc/starting.txt39
-rw-r--r--runtime/doc/syntax.txt10
-rw-r--r--runtime/doc/treesitter.txt7
-rw-r--r--runtime/doc/various.txt19
-rw-r--r--runtime/doc/vim_diff.txt6
-rw-r--r--runtime/doc/visual.txt4
-rw-r--r--runtime/doc/windows.txt6
-rw-r--r--runtime/filetype.vim99
-rw-r--r--runtime/ftplugin/git.vim41
-rw-r--r--runtime/ftplugin/gitcommit.vim47
-rw-r--r--runtime/ftplugin/gitrebase.vim15
-rw-r--r--runtime/lua/vim/diagnostic.lua21
-rw-r--r--runtime/lua/vim/filetype.lua145
-rw-r--r--runtime/lua/vim/highlight.lua19
-rw-r--r--runtime/lua/vim/lsp.lua227
-rw-r--r--runtime/lua/vim/lsp/buf.lua24
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua4
-rw-r--r--runtime/lua/vim/lsp/handlers.lua88
-rw-r--r--runtime/lua/vim/lsp/util.lua138
-rw-r--r--runtime/lua/vim/treesitter.lua1
-rw-r--r--runtime/lua/vim/treesitter/language.lua2
-rw-r--r--runtime/lua/vim/treesitter/query.lua27
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim23
-rw-r--r--runtime/scripts.vim8
-rw-r--r--runtime/syntax/git.vim93
-rw-r--r--runtime/syntax/gitcommit.vim86
-rw-r--r--runtime/syntax/gitrebase.vim15
-rw-r--r--runtime/syntax/i3config.vim7
-rw-r--r--runtime/syntax/rc.vim21
-rw-r--r--runtime/syntax/scala.vim110
-rw-r--r--src/nvim/api/deprecated.c11
-rw-r--r--src/nvim/api/extmark.c162
-rw-r--r--src/nvim/api/keysets.lua2
-rw-r--r--src/nvim/api/private/converter.c21
-rw-r--r--src/nvim/api/private/helpers.c13
-rw-r--r--src/nvim/api/vim.c25
-rw-r--r--src/nvim/buffer.c16
-rw-r--r--src/nvim/buffer_defs.h7
-rw-r--r--src/nvim/change.c2
-rw-r--r--src/nvim/channel.h2
-rw-r--r--src/nvim/cursor.c8
-rw-r--r--src/nvim/decoration.c161
-rw-r--r--src/nvim/decoration.h7
-rw-r--r--src/nvim/diff.c5
-rw-r--r--src/nvim/edit.c38
-rw-r--r--src/nvim/eval.c10
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/eval/typval.h3
-rw-r--r--src/nvim/ex_cmds.c66
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_cmds2.c10
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c96
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/extmark.c298
-rw-r--r--src/nvim/extmark.h5
-rw-r--r--src/nvim/extmark_defs.h12
-rw-r--r--src/nvim/file_search.c6
-rw-r--r--src/nvim/fileio.c48
-rw-r--r--src/nvim/getchar.c288
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/input.c2
-rw-r--r--src/nvim/keymap.c2
-rw-r--r--src/nvim/keymap.h3
-rw-r--r--src/nvim/lua/converter.c8
-rw-r--r--src/nvim/lua/executor.c10
-rw-r--r--src/nvim/lua/treesitter.c4
-rw-r--r--src/nvim/main.c19
-rw-r--r--src/nvim/map.c5
-rw-r--r--src/nvim/map.h10
-rw-r--r--src/nvim/marktree.c201
-rw-r--r--src/nvim/marktree.h90
-rw-r--r--src/nvim/mbyte.c10
-rw-r--r--src/nvim/memfile.c6
-rw-r--r--src/nvim/memline.c6
-rw-r--r--src/nvim/message.c15
-rw-r--r--src/nvim/normal.c78
-rw-r--r--src/nvim/ops.c167
-rw-r--r--src/nvim/option.c103
-rw-r--r--src/nvim/option.h20
-rw-r--r--src/nvim/option_defs.h13
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/os/input.c14
-rw-r--r--src/nvim/path.c28
-rw-r--r--src/nvim/po/ja.euc-jp.po9
-rw-r--r--src/nvim/po/ja.po9
-rw-r--r--src/nvim/regexp.c2
-rw-r--r--src/nvim/regexp_nfa.c7
-rw-r--r--src/nvim/screen.c94
-rw-r--r--src/nvim/spell.c2
-rw-r--r--src/nvim/state.c11
-rw-r--r--src/nvim/strings.c28
-rw-r--r--src/nvim/terminal.c2
-rw-r--r--src/nvim/testdir/check.vim6
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_autocmd.vim34
-rw-r--r--src/nvim/testdir/test_buffer.vim31
-rw-r--r--src/nvim/testdir/test_cmdline.vim8
-rw-r--r--src/nvim/testdir/test_compiler.vim3
-rw-r--r--src/nvim/testdir/test_conceal.vim4
-rw-r--r--src/nvim/testdir/test_diffmode.vim29
-rw-r--r--src/nvim/testdir/test_edit.vim23
-rw-r--r--src/nvim/testdir/test_ex_mode.vim10
-rw-r--r--src/nvim/testdir/test_expr.vim65
-rw-r--r--src/nvim/testdir/test_filetype.vim217
-rw-r--r--src/nvim/testdir/test_filetype_lua.vim2
-rw-r--r--src/nvim/testdir/test_filter_map.vim10
-rw-r--r--src/nvim/testdir/test_help.vim8
-rw-r--r--src/nvim/testdir/test_ins_complete.vim22
-rw-r--r--src/nvim/testdir/test_listchars.vim32
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.vim10
-rw-r--r--src/nvim/testdir/test_marks.vim17
-rw-r--r--src/nvim/testdir/test_normal.vim20
-rw-r--r--src/nvim/testdir/test_options.vim11
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim8
-rw-r--r--src/nvim/testdir/test_regexp_utf8.vim8
-rw-r--r--src/nvim/testdir/test_registers.vim318
-rw-r--r--src/nvim/testdir/test_retab.vim3
-rw-r--r--src/nvim/testdir/test_scriptnames.vim6
-rw-r--r--src/nvim/testdir/test_selectmode.vim57
-rw-r--r--src/nvim/testdir/test_spell.vim8
-rw-r--r--src/nvim/testdir/test_statusline.vim9
-rw-r--r--src/nvim/testdir/test_timers.vim23
-rw-r--r--src/nvim/testdir/test_usercommands.vim4
-rw-r--r--src/nvim/testdir/test_utf8.vim52
-rw-r--r--src/nvim/testdir/test_virtualedit.vim262
-rw-r--r--src/nvim/testdir/test_visual.vim79
-rw-r--r--src/nvim/types.h2
-rw-r--r--src/nvim/undo.c4
-rw-r--r--test/functional/api/command_spec.lua14
-rw-r--r--test/functional/api/extmark_spec.lua71
-rw-r--r--test/functional/api/keymap_spec.lua10
-rw-r--r--test/functional/api/vim_spec.lua8
-rw-r--r--test/functional/core/channels_spec.lua32
-rw-r--r--test/functional/core/startup_spec.lua23
-rw-r--r--test/functional/editor/K_spec.lua23
-rw-r--r--test/functional/editor/meta_key_spec.lua17
-rw-r--r--test/functional/editor/mode_insert_spec.lua53
-rw-r--r--test/functional/editor/put_spec.lua16
-rw-r--r--test/functional/helpers.lua1
-rw-r--r--test/functional/legacy/listchars_spec.lua81
-rw-r--r--test/functional/legacy/visual_mode_spec.lua44
-rw-r--r--test/functional/lua/diagnostic_spec.lua24
-rw-r--r--test/functional/lua/filetype_spec.lua575
-rw-r--r--test/functional/lua/vim_spec.lua128
-rw-r--r--test/functional/plugin/lsp_spec.lua183
-rw-r--r--test/functional/terminal/buffer_spec.lua5
-rw-r--r--test/functional/terminal/mouse_spec.lua30
-rw-r--r--test/functional/treesitter/parser_spec.lua36
-rw-r--r--test/functional/ui/bufhl_spec.lua18
-rw-r--r--test/functional/ui/input_spec.lua21
-rw-r--r--test/functional/ui/popupmenu_spec.lua148
-rw-r--r--test/unit/marktree_spec.lua44
-rw-r--r--test/unit/path_spec.lua16
-rw-r--r--third-party/CMakeLists.txt4
192 files changed, 4802 insertions, 2370 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index 7d41c17894..0000000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-/.github/ @jamessan
-/ci/ @jamessan
-/scripts/ @jamessan
diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml
index ee72cf5f01..39d85e967d 100644
--- a/.github/workflows/api-docs.yml
+++ b/.github/workflows/api-docs.yml
@@ -48,6 +48,6 @@ jobs:
if: ${{ steps.docs.outputs.UPDATED_DOCS != 0 }}
run: |
git add -u
- git commit -m 'docs: regenerate'
+ git commit -m 'docs: regenerate [skip ci]'
git push --force https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY} ${DOC_BRANCH}
- gh pr create --fill --base ${GITHUB_REF#refs/heads/} --head ${DOC_BRANCH} || true
+ gh pr create --draft --fill --base ${GITHUB_REF#refs/heads/} --head ${DOC_BRANCH} || true
diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml
index 6c74ec99d3..5044b7dcab 100644
--- a/.github/workflows/commitlint.yml
+++ b/.github/workflows/commitlint.yml
@@ -1,13 +1,13 @@
name: "Commit Linter"
on:
- pull_request:
+ # Only pull_request and push honor [skip ci]. Since this workflow must pass
+ # to merge a PR, it can't be skipped, so use pull_request_target
+ pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
lint-commits:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
with:
diff --git a/.github/workflows/env.sh b/.github/workflows/env.sh
index d1bc412399..0964995605 100755
--- a/.github/workflows/env.sh
+++ b/.github/workflows/env.sh
@@ -35,13 +35,14 @@ case "$FLAVOR" in
cat <<EOF >> "$GITHUB_ENV"
CLANG_SANITIZER=ASAN_UBSAN
SYMBOLIZER=asan_symbolize-13
-ASAN_OPTIONS=detect_leaks=1:check_initialization_order=1:log_path=$GITHUB_WORKSPACE/build/log/asan
+ASAN_OPTIONS=detect_leaks=1:check_initialization_order=1:log_path=$GITHUB_WORKSPACE/build/log/asan:intercept_tls_get_addr=0
UBSAN_OPTIONS=print_stacktrace=1 log_path=$GITHUB_WORKSPACE/build/log/ubsan
EOF
;;
tsan)
cat <<EOF >> "$GITHUB_ENV"
TSAN_OPTIONS=log_path=$GITHUB_WORKSPACE/build/log/tsan
+CLANG_SANITIZER=TSAN
EOF
;;
lint)
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index 76fc8793fa..3de0c453a5 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -1,8 +1,9 @@
name: "Pull Request Labeler"
on:
pull_request_target:
- types: opened
+ types: [opened]
jobs:
+
triage:
runs-on: ubuntu-latest
permissions:
@@ -12,8 +13,10 @@ jobs:
- uses: actions/labeler@main
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
+
type-scope:
runs-on: ubuntu-latest
+ needs: ["triage"]
permissions:
contents: write
pull-requests: write
@@ -23,8 +26,9 @@ jobs:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
steps:
- # Extract type and try to add it as a label
- - run: gh pr edit "$PR_NUMBER" --add-label "$(echo "$PR_TITLE" | sed -E 's|([[:alpha:]]+)(\(.*\))?!?:.*|\1|')" || true
-
- # Extract scope and try to add it as a label
- - run: gh pr edit "$PR_NUMBER" --add-label "$(echo "$PR_TITLE" | sed -E 's|[[:alpha:]]+\((.+)\)!?:.*|\1|')" || true
+ - name: "Extract commit type and add as label"
+ continue-on-error: true
+ run: gh pr edit "$PR_NUMBER" --add-label "$(echo "$PR_TITLE" | sed -E 's|([[:alpha:]]+)(\(.*\))?!?:.*|\1|')"
+ - name: "Extract commit scope and add as label"
+ continue-on-error: true
+ run: gh pr edit "$PR_NUMBER" --add-label "$(echo "$PR_TITLE" | sed -E 's|[[:alpha:]]+\((.+)\)!?:.*|\1|')"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5ad4c90eaf..b72c2ab71d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -212,6 +212,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NVIM_VERSION: ${{ needs.linux.outputs.version }}
+ DEBUG: api
run: |
envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md"
gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos/* nvim-linux64/* appimage/* nvim-win64/*
diff --git a/.github/workflows/vim-patches.yml b/.github/workflows/vim-patches.yml
index 5742b51158..453d293b0e 100644
--- a/.github/workflows/vim-patches.yml
+++ b/.github/workflows/vim-patches.yml
@@ -49,4 +49,4 @@ jobs:
git add -u
git commit -m 'version.c: update [skip ci]'
git push --force https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY} ${VERSION_BRANCH}
- gh pr create --fill --label vim-patch --base ${GITHUB_REF#refs/heads/} --head ${VERSION_BRANCH} || true
+ gh pr create --draft --fill --label vim-patch --base ${GITHUB_REF#refs/heads/} --head ${VERSION_BRANCH} || true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0c43c5c494..e0f05e1205 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -408,6 +408,19 @@ main(void)
if(TS_HAS_SET_MATCH_LIMIT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_TS_HAS_SET_MATCH_LIMIT")
endif()
+check_c_source_compiles("
+#include <stdlib.h>
+#include <tree_sitter/api.h>
+int
+main(void)
+{
+ ts_set_allocator(malloc, calloc, realloc, free);
+ return 0;
+}
+" TS_HAS_SET_ALLOCATOR)
+if(TS_HAS_SET_ALLOCATOR)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_TS_HAS_SET_ALLOCATOR")
+endif()
# Note: The test lib requires LuaJIT; it will be skipped if LuaJIT is missing.
option(PREFER_LUA "Prefer Lua over LuaJIT in the nvim executable." OFF)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b02aeb1ed1..5e04f33b15 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,11 +7,11 @@ Getting started
If you want to help but don't know where to start, here are some
low-risk/isolated tasks:
-- [Merge a Vim patch].
- Try a [complexity:low] issue.
- Fix bugs found by [Clang](#clang-scan-build), [PVS](#pvs-studio) or
[Coverity](#coverity).
- [Improve documentation][wiki-contribute-help]
+- [Merge a Vim patch] (Familiarity with Vim is *strongly* recommended)
Reporting problems
------------------
diff --git a/ci/build.ps1 b/ci/build.ps1
index a81d351bc6..ef5ba3bf2d 100644
--- a/ci/build.ps1
+++ b/ci/build.ps1
@@ -86,19 +86,10 @@ elseif ($compiler -eq 'MSVC') {
}
if (-not $NoTests) {
- # Setup python (use AppVeyor system python)
-
- # Disambiguate python3, if needed
- if (-not (Test-Path -Path C:\hostedtoolcache\windows\Python\3.5.4\x64\python3.exe) ) {
- move C:\hostedtoolcache\windows\Python\3.5.4\x64\python.exe C:\hostedtoolcache\windows\Python\3.5.4\x64\python3.exe
- }
- $env:PATH = "C:\hostedtoolcache\windows\Python\2.7.18\x64;C:\hostedtoolcache\windows\Python\3.5.4\x64;$env:PATH"
-
+ python -m ensurepip
python -m pip install pynvim ; exitIfFailed
- python3 -m pip install pynvim ; exitIfFailed
# Sanity check
python -c "import pynvim; print(str(pynvim))" ; exitIfFailed
- python3 -c "import pynvim; print(str(pynvim))" ; exitIfFailed
gem.cmd install --pre neovim
Get-Command -CommandType Application neovim-ruby-host.bat
diff --git a/ci/common/submit_coverage.sh b/ci/common/submit_coverage.sh
index 9c7887de0b..cb6ab62b5b 100755
--- a/ci/common/submit_coverage.sh
+++ b/ci/common/submit_coverage.sh
@@ -18,12 +18,12 @@ if ! [ -f "$codecov_sh" ]; then
curl --retry 5 --silent --fail -o "$codecov_sh" https://codecov.io/bash
chmod +x "$codecov_sh"
- python3 -m pip install --quiet --user gcovr
+ python -m pip install --quiet --user gcovr
fi
(
cd build
- python3 -m gcovr --branches --exclude-unreachable-branches --print-summary -j 2 --exclude '.*/auto/.*' --root .. --delete -o ../coverage.xml --xml
+ python -m gcovr --branches --exclude-unreachable-branches --print-summary -j 2 --exclude '.*/auto/.*' --root .. --delete -o ../coverage.xml --xml
)
# Upload to codecov.
diff --git a/ci/install.sh b/ci/install.sh
index bd42274b49..894e090de2 100755
--- a/ci/install.sh
+++ b/ci/install.sh
@@ -4,12 +4,8 @@ set -e
set -o pipefail
# Use default CC to avoid compilation problems when installing Python modules.
-echo "Install neovim module for Python 3."
-CC=cc python3 -m pip -q install --user --upgrade pynvim
-if python2 -m pip -c True 2>&1; then
- echo "Install neovim module for Python 2."
- CC=cc python2 -m pip -q install --user --upgrade pynvim
-fi
+echo "Install neovim module for Python."
+CC=cc python -m pip -q install --user --upgrade pynvim
echo "Install neovim RubyGem."
gem install --no-document --bindir "$HOME/.local/bin" --user-install --pre neovim
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index 9933ace8c2..86c71fa52d 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: 2021 Dec 17
+" Last Change: 2022 Jan 11
" These functions are moved here from runtime/filetype.vim to make startup
" faster.
@@ -67,13 +67,29 @@ func dist#ft#FTasmsyntax()
endif
endfunc
-" Check if one of the first five lines contains "VB_Name". In that case it is
-" probably a Visual Basic file. Otherwise it's assumed to be "alt" filetype.
-func dist#ft#FTVB(alt)
- if getline(1).getline(2).getline(3).getline(4).getline(5) =~? 'VB_Name\|Begin VB\.\(Form\|MDIForm\|UserControl\)'
+func dist#ft#FTbas()
+ if exists("g:filetype_bas")
+ exe "setf " . g:filetype_bas
+ return
+ endif
+
+ " most frequent FreeBASIC-specific keywords in distro files
+ let fb_keywords = '\c^\s*\%(extern\|var\|enum\|private\|scope\|union\|byref\|operator\|constructor\|delete\|namespace\|public\|property\|with\|destructor\|using\)\>\%(\s*[:=(]\)\@!'
+ let fb_preproc = '\c^\s*\%(#\a\+\|option\s\+\%(byval\|dynamic\|escape\|\%(no\)\=gosub\|nokeyword\|private\|static\)\>\)'
+ let fb_comment = "^\\s*/'"
+ " OPTION EXPLICIT, without the leading underscore, is common to many dialects
+ let qb64_preproc = '\c^\s*\%($\a\+\|option\s\+\%(_explicit\|_\=explicitarray\)\>\)'
+
+ let lines = getline(1, min([line("$"), 100]))
+
+ if match(lines, fb_preproc) > -1 || match(lines, fb_comment) > -1 || match(lines, fb_keywords) > -1
+ setf freebasic
+ elseif match(lines, qb64_preproc) > -1
+ setf qb64
+ elseif match(lines, '\cVB_Name\|Begin VB\.\(Form\|MDIForm\|UserControl\)') > -1
setf vb
else
- exe "setf " . a:alt
+ setf basic
endif
endfunc
@@ -829,6 +845,38 @@ func dist#ft#Dep3patch()
endfor
endfunc
+" This function checks the first 15 lines for appearance of 'FoamFile'
+" and then 'object' in a following line.
+" In that case, it's probably an OpenFOAM file
+func dist#ft#FTfoam()
+ let ffile = 0
+ let lnum = 1
+ while lnum <= 15
+ if getline(lnum) =~# '^FoamFile'
+ let ffile = 1
+ elseif ffile == 1 && getline(lnum) =~# '^\s*object'
+ setf foam
+ return
+ endif
+ let lnum = lnum + 1
+ endwhile
+endfunc
+
+" Determine if a *.tf file is TF mud client or terraform
+func dist#ft#FTtf()
+ let numberOfLines = line('$')
+ for i in range(1, numberOfLines)
+ let currentLine = trim(getline(i))
+ let firstCharacter = currentLine[0]
+ if firstCharacter !=? ";" && firstCharacter !=? "/" && firstCharacter !=? ""
+ setf terraform
+ return
+ endif
+ endfor
+ setf tf
+endfunc
+
+
" Restore 'cpoptions'
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim
index 90d353f9de..b28170b7a1 100644
--- a/runtime/autoload/man.vim
+++ b/runtime/autoload/man.vim
@@ -7,7 +7,6 @@ let s:loaded_man = 1
let s:find_arg = '-w'
let s:localfile_arg = v:true " Always use -l if possible. #6683
-let s:section_arg = '-S'
function! man#init() abort
try
@@ -216,16 +215,42 @@ endfunction
function! s:get_path(sect, name) abort
" Some man implementations (OpenBSD) return all available paths from the
- " search command, so we get() the first one. #8341
+ " search command. Previously, this function would simply select the first one.
+ "
+ " However, some searches will report matches that are incorrect:
+ " man -w strlen may return string.3 followed by strlen.3, and therefore
+ " selecting the first would get us the wrong page. Thus, we must find the
+ " first matching one.
+ "
+ " There's yet another special case here. Consider the following:
+ " If you run man -w strlen and string.3 comes up first, this is a problem. We
+ " should search for a matching named one in the results list.
+ " However, if you search for man -w clock_gettime, you will *only* get
+ " clock_getres.2, which is the right page. Searching the resuls for
+ " clock_gettime will no longer work. In this case, we should just use the
+ " first one that was found in the correct section.
+ "
+ " Finally, we can avoid relying on -S or -s here since they are very
+ " inconsistently supported. Instead, call -w with a section and a name.
if empty(a:sect)
- return substitute(get(split(s:system(['man', s:find_arg, a:name])), 0, ''), '\n\+$', '', '')
+ let results = split(s:system(['man', s:find_arg, a:name]))
+ else
+ let results = split(s:system(['man', s:find_arg, a:sect, a:name]))
+ endif
+
+ if empty(results)
+ return ''
endif
- " '-s' flag handles:
- " - tokens like 'printf(echo)'
- " - sections starting with '-'
- " - 3pcap section (found on macOS)
- " - commas between sections (for section priority)
- return substitute(get(split(s:system(['man', s:find_arg, s:section_arg, a:sect, a:name])), 0, ''), '\n\+$', '', '')
+
+ " find any that match the specified name
+ let namematches = filter(copy(results), 'fnamemodify(v:val, ":t") =~ a:name')
+ let sectmatches = []
+
+ if !empty(namematches) && !empty(a:sect)
+ let sectmatches = filter(copy(namematches), 'fnamemodify(v:val, ":e") == a:sect')
+ endif
+
+ return substitute(get(sectmatches, 0, get(namematches, 0, results[0])), '\n\+$', '', '')
endfunction
" s:verify_exists attempts to find the path to a manpage
@@ -243,40 +268,72 @@ endfunction
" then we don't do it again in step 2.
function! s:verify_exists(sect, name) abort
let sect = a:sect
- if empty(sect)
- let sect = get(b:, 'man_default_sects', '')
- endif
- try
- return s:get_path(sect, a:name)
- catch /^command error (/
- endtry
-
- if !empty(get(b:, 'man_default_sects', '')) && sect !=# b:man_default_sects
+ if empty(sect)
+ " no section specified, so search with b:man_default_sects
+ if exists('b:man_default_sects')
+ let sects = split(b:man_default_sects, ',')
+ for sec in sects
+ try
+ let res = s:get_path(sec, a:name)
+ if !empty(res)
+ return res
+ endif
+ catch /^command error (/
+ endtry
+ endfor
+ endif
+ else
+ " try with specified section
try
- return s:get_path(b:man_default_sects, a:name)
+ let res = s:get_path(sect, a:name)
+ if !empty(res)
+ return res
+ endif
catch /^command error (/
endtry
- endif
- if !empty(sect)
- try
- return s:get_path('', a:name)
- catch /^command error (/
- endtry
+ " try again with b:man_default_sects
+ if exists('b:man_default_sects')
+ let sects = split(b:man_default_sects, ',')
+ for sec in sects
+ try
+ let res = s:get_path(sec, a:name)
+ if !empty(res)
+ return res
+ endif
+ catch /^command error (/
+ endtry
+ endfor
+ endif
endif
+ " if none of the above worked, we will try with no section
+ try
+ let res = s:get_path('', a:name)
+ if !empty(res)
+ return res
+ endif
+ catch /^command error (/
+ endtry
+
+ " if that still didn't work, we will check for $MANSECT and try again with it
+ " unset
if !empty($MANSECT)
try
let MANSECT = $MANSECT
call setenv('MANSECT', v:null)
- return s:get_path('', a:name)
+ let res = s:get_path('', a:name)
+ if !empty(res)
+ return res
+ endif
catch /^command error (/
finally
call setenv('MANSECT', MANSECT)
endtry
endif
+ " finally, if that didn't work, there is no hope
throw 'no manual entry for ' . a:name
endfunction
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 0daca0de53..2da1f5e40d 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -709,8 +709,8 @@ 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|,
it writes to Nvim's stdout. For an internal terminal instance
- (|nvim_open_term()|) it writes directly to terimal output. See
- |channel-bytes| for more information.
+ (|nvim_open_term()|) it writes directly to terminal output.
+ See |channel-bytes| for more information.
This function writes raw data, not RPC messages. If the
channel was created with `rpc=true` then the channel expects
@@ -1405,7 +1405,7 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
{from_part} Legacy Vim parameter. Usually true.
{do_lt} Also translate <lt>. Ignored if `special` is
false.
- {special} Replace |keycodes|, e.g. <CR> becomes a "\n"
+ {special} Replace |keycodes|, e.g. <CR> becomes a "\r"
char.
See also: ~
@@ -2548,6 +2548,10 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
• priority: a priority value for the highlight
group. For example treesitter highlighting
uses a value of 100.
+ • strict: boolean that indicates extmark should
+ not be placed if the line or column value is
+ past the end of the buffer or end of the line
+ respectively. Defaults to true.
Return: ~
Id of the created/updated extmark
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 46d9a3b57a..5e50f9c1f8 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -40,10 +40,10 @@ effects. Be careful not to destroy your text.
2. Defining autocommands *autocmd-define*
*:au* *:autocmd*
-:au[tocmd] [group] {event} {pat} [++once] [++nested] {cmd}
+:au[tocmd] [group] {event} {aupat} [++once] [++nested] {cmd}
Add {cmd} to the list of commands that Vim will
execute automatically on {event} for a file matching
- {pat} |autocmd-pattern|.
+ {aupat} |autocmd-pattern|.
Note: A quote character is seen as argument to the
:autocmd and won't start a comment.
Nvim always adds {cmd} after existing autocommands so
@@ -119,19 +119,19 @@ prompt. When one command outputs two messages this can happen anyway.
==============================================================================
3. Removing autocommands *autocmd-remove*
-:au[tocmd]! [group] {event} {pat} [++once] [++nested] {cmd}
+:au[tocmd]! [group] {event} {aupat} [++once] [++nested] {cmd}
Remove all autocommands associated with {event} and
- {pat}, and add the command {cmd}.
+ {aupat}, and add the command {cmd}.
See |autocmd-once| for [++once].
See |autocmd-nested| for [++nested].
-:au[tocmd]! [group] {event} {pat}
+:au[tocmd]! [group] {event} {aupat}
Remove all autocommands associated with {event} and
- {pat}.
+ {aupat}.
-:au[tocmd]! [group] * {pat}
- Remove all autocommands associated with {pat} for all
- events.
+:au[tocmd]! [group] * {aupat}
+ Remove all autocommands associated with {aupat} for
+ all events.
:au[tocmd]! [group] {event}
Remove ALL autocommands for {event}.
@@ -151,12 +151,12 @@ with ":augroup"); otherwise, Vim uses the group defined with [group].
==============================================================================
4. Listing autocommands *autocmd-list*
-:au[tocmd] [group] {event} {pat}
+:au[tocmd] [group] {event} {aupat}
Show the autocommands associated with {event} and
- {pat}.
+ {aupat}.
-:au[tocmd] [group] * {pat}
- Show the autocommands associated with {pat} for all
+:au[tocmd] [group] * {aupat}
+ Show the autocommands associated with {aupat} for all
events.
:au[tocmd] [group] {event}
@@ -1075,16 +1075,16 @@ WinScrolled After scrolling the viewport of the current
==============================================================================
-6. Patterns *autocmd-pattern* *{pat}*
+6. Patterns *autocmd-pattern* *{aupat}*
-The {pat} argument can be a comma separated list. This works as if the
-command was given with each pattern separately. Thus this command: >
+The {aupat} argument of `:autocmd` can be a comma separated list. This works
+as if the command was given with each pattern separately. Thus this command: >
:autocmd BufRead *.txt,*.info set et
Is equivalent to: >
:autocmd BufRead *.txt set et
:autocmd BufRead *.info set et
-The file pattern {pat} is tested for a match against the file name in one of
+The file pattern {aupat} is tested for a match against the file name in one of
two ways:
1. When there is no '/' in the pattern, Vim checks for a match against only
the tail part of the file name (without its leading directory path).
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 7716af25bd..641cd93386 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -697,7 +697,8 @@ Line numbers may be specified with: *:range* *{address}*
Each may be followed (several times) by '+' or '-' and an optional number.
This number is added or subtracted from the preceding line number. If the
-number is omitted, 1 is used.
+number is omitted, 1 is used. If there is nothing before the '+' or '-' then
+the current line is used.
The "/" and "?" after {pattern} are required to separate the pattern from
anything that follows.
@@ -727,7 +728,7 @@ Some commands allow for a count after the command. This count is used as the
number of lines to be used, starting with the line given in the last line
specifier (the default is the cursor line). The commands that accept a count
are the ones that use a range but do not have a file name argument (because
-a file name can also be a number).
+a file name can also be a number). The count cannot be negative.
Examples: >
:s/x/X/g 5 substitute 'x' by 'X' in the current line and four
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index bb36fa46f6..19db3158be 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -334,8 +334,9 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
that returns any of the above.
Parameters: ~
- {opts} table Configuration table with the following
- keys:
+ {opts} table|nil When omitted or "nil", retrieve the
+ current configuration. Otherwise, a
+ configuration table with the following keys:
• underline: (default true) Use underline for
diagnostics. Options:
• severity: Only underline diagnostics
diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt
index 6115a5d235..abe99102ee 100644
--- a/runtime/doc/diff.txt
+++ b/runtime/doc/diff.txt
@@ -324,8 +324,9 @@ After setting this variable, reload the syntax script: >
FINDING THE DIFFERENCES *diff-diffexpr*
-The 'diffexpr' option can be set to use something else than the standard
-"diff" program to compare two files and find the differences. *E959*
+The 'diffexpr' option can be set to use something else than the internal diff
+support or the standard "diff" program to compare two files and find the
+differences.
When 'diffexpr' is empty, Vim uses this command to find the differences
between file1 and file2: >
@@ -358,7 +359,7 @@ format mentioned. These variables are set to the file names used:
v:fname_in original file
v:fname_new new version of the same file
- v:fname_out resulting diff file
+ v:fname_out where to write the resulting diff file
Additionally, 'diffexpr' should take care of "icase" and "iwhite" in the
'diffopt' option. 'diffexpr' cannot change the value of 'lines' and
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 14df41e6c8..44987f3b7b 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -845,7 +845,7 @@ Note: When the 'write' option is off, you are not able to write any file.
*:w* *:write*
*E502* *E503* *E504* *E505*
- *E512* *E514* *E667* *E796* *E949*
+ *E512* *E514* *E667* *E949*
:w[rite] [++opt] Write the whole buffer to the current file. This is
the normal way to save changes to a file. It fails
when the 'readonly' option is set or when there is
@@ -1331,6 +1331,7 @@ current directory for that window. Windows where the |:lcd| command has not
been used stick to the global or tab-local directory. When jumping to another
window the current directory is changed to the last specified local current
directory. If none was specified, the global or tab-local directory is used.
+When creating a new window it inherits the local directory of the current window.
When changing tabs the same behaviour applies. If the current tab has no
local working directory the global working directory is used.
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index c2158f3a1e..fa75ead9a3 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -111,7 +111,7 @@ When mixing Number and Float the Number is converted to Float. Otherwise
there is no automatic conversion of Float. You can use str2float() for String
to Float, printf() for Float to String and float2nr() for Float to Number.
- *E891* *E892* *E893* *E894*
+ *E362* *E891* *E892* *E893* *E894* *E907*
When expecting a Float a Number can also be used, but nothing else.
*no-type-checking*
@@ -1195,6 +1195,7 @@ When expr8 is a |Funcref| type variable, invoke the function it refers to.
expr8->name([args]) method call *method* *->*
expr8->{lambda}([args])
+ *E260* *E276*
For methods that are also available as global functions this is the same as: >
name(expr8 [, args])
There can also be methods specifically for the type of "expr8".
@@ -4574,8 +4575,10 @@ funcref({name} [, {arglist}] [, {dict}])
function {name} is redefined later.
Unlike |function()|, {name} must be an existing user function.
- Also for autoloaded functions. {name} cannot be a builtin
- function.
+ It only works for an autoloaded function if it has already
+ been loaded (to avoid mistakenly loading the autoload script
+ when only intending to use the function name, use |function()|
+ instead). {name} cannot be a builtin function.
Can also be used as a |method|: >
GetFuncname()->funcref([arg])
@@ -6892,7 +6895,7 @@ match({expr}, {pat} [, {start} [, {count}]]) *match()*
GetText()->match('word')
GetList()->match('word')
<
- *matchadd()* *E798* *E799* *E801* *E957*
+ *matchadd()* *E798* *E799* *E801* *E957*
matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
Defines a pattern to be highlighted in the current window (a
"match"). It will be highlighted with {group}. Returns an
@@ -11063,7 +11066,7 @@ See |:verbose-cmd| for more information.
command, use line breaks instead of |:bar|: >
:exe "func Foo()\necho 'foo'\nendfunc"
<
- *:delf* *:delfunction* *E130* *E131* *E933*
+ *:delf* *:delfunction* *E131* *E933*
:delf[unction][!] {name}
Delete function {name}.
{name} can also be a |Dictionary| entry that is a
@@ -13153,7 +13156,7 @@ code can be used: >
unlet scriptnames_output
==============================================================================
-The sandbox *eval-sandbox* *sandbox* *E48*
+The sandbox *eval-sandbox* *sandbox*
The 'foldexpr', 'formatexpr', 'includeexpr', 'indentexpr', 'statusline' and
'foldtext' options may be evaluated in a sandbox. This means that you are
@@ -13162,6 +13165,7 @@ safety for when these options are set from a modeline. It is also used when
the command from a tags file is executed and for CTRL-R = in the command line.
The sandbox is also used for the |:sandbox| command.
+ *E48*
These items are not allowed in the sandbox:
- changing the buffer text
- defining or changing mapping, autocommands, user commands
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index fdd9c8c12e..5486c87af9 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -131,14 +131,15 @@ shell script: "#!/bin/csh".
argument was used.
*filetype-overrule*
-When the same extension is used for two filetypes, Vim tries to guess what
-kind of file it is. This doesn't always work. A number of global variables
-can be used to overrule the filetype used for certain extensions:
+When the same extension is used for multiple filetypes, Vim tries to guess
+what kind of file it is. This doesn't always work. A number of global
+variables can be used to overrule the filetype used for certain extensions:
file name variable ~
*.asa g:filetype_asa |ft-aspvbs-syntax| |ft-aspperl-syntax|
*.asm g:asmsyntax |ft-asm-syntax|
*.asp g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax|
+ *.bas g:filetype_bas |ft-basic-syntax|
*.fs g:filetype_fs |ft-forth-syntax|
*.i g:filetype_i |ft-progress-syntax|
*.inc g:filetype_inc
diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt
index 80c934d13b..8bc47a3b10 100644
--- a/runtime/doc/fold.txt
+++ b/runtime/doc/fold.txt
@@ -501,7 +501,9 @@ Note the use of backslashes to avoid some characters to be interpreted by the
:endfunction
Evaluating 'foldtext' is done in the |sandbox|. The current window is set to
-the window that displays the line. Errors are ignored.
+the window that displays the line.
+
+Errors are ignored. For debugging set the 'debug' option to "throw".
The default value is |foldtext()|. This returns a reasonable text for most
types of folding. If you don't like it, you can specify your own 'foldtext'
diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt
index 9cc7d063a8..569995d319 100644
--- a/runtime/doc/helphelp.txt
+++ b/runtime/doc/helphelp.txt
@@ -219,7 +219,7 @@ command: >
<
*:helpt* *:helptags*
- *E154* *E150* *E151* *E152* *E153* *E670* *E856*
+ *E150* *E151* *E152* *E153* *E154* *E670* *E856*
:helpt[ags] [++t] {dir}
Generate the help tags file(s) for directory {dir}.
When {dir} is ALL then all "doc" directories in
@@ -358,7 +358,7 @@ When referring to a Vim option in the help file, place the option name between
two single quotes, eg. 'statusline'
When referring to any other technical term, such as a filename or function
-parameter, surround it in backticks (`), eg. `~/.path/to/init.vim`.
+parameter, surround it in backticks, eg. `~/.path/to/init.vim`.
HIGHLIGHTING
diff --git a/runtime/doc/if_cscop.txt b/runtime/doc/if_cscop.txt
index f05b3bb8ed..8947aefc1b 100644
--- a/runtime/doc/if_cscop.txt
+++ b/runtime/doc/if_cscop.txt
@@ -40,7 +40,7 @@ See |cscope-usage| to get started.
==============================================================================
Cscope commands *cscope-commands*
- *:cscope* *:cs* *:scs* *:scscope* *E259* *E262* *E561* *E560*
+ *:cscope* *:cs* *:scs* *:scscope* *E259* *E262* *E560* *E561*
All cscope commands are accessed through suboptions to the cscope commands.
`:cscope` or `:cs` is the main command
`:scscope` or `:scs` does the same and splits the window
diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt
index 1b42092616..a76f8636f8 100644
--- a/runtime/doc/indent.txt
+++ b/runtime/doc/indent.txt
@@ -872,7 +872,7 @@ For example, with N = 1, this will give:
*PHP_outdentphpescape*
To indent PHP escape tags as the surrounding non-PHP code (only affects the
PHP escape tags): >
-:let g:PHP_outdentphpescape = 0
+ :let g:PHP_outdentphpescape = 0
-------------
*PHP_removeCRwhenUnix*
@@ -1199,7 +1199,7 @@ comments will be indented according to the correctly indented code.
VIM *ft-vim-indent*
-
+ *g:vim_indent_cont*
For indenting Vim scripts there is one variable that specifies the amount of
indent for a continuation line, a line that starts with a backslash: >
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index d74d7cafa8..ae2b9c4418 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -830,7 +830,7 @@ space is preferred). Maximum line length is 510 bytes.
For an example, imagine the 'thesaurus' file has a line like this: >
angry furious mad enraged
-<Placing the cursor after the letters "ang" and typing CTRL-X CTRL-T would
+Placing the cursor after the letters "ang" and typing CTRL-X CTRL-T would
complete the word "angry"; subsequent presses would change the word to
"furious", "mad" etc.
@@ -842,7 +842,7 @@ https://github.com/vim/vim/issues/629#issuecomment-443293282
Unpack thesaurus_pkg.zip, put the thesaurus.txt file somewhere, e.g.
~/.vim/thesaurus/english.txt, and the 'thesaurus' option to this file name.
-
+
Completing keywords with 'thesaurusfunc' *compl-thesaurusfunc*
If the 'thesaurusfunc' option is set, then the user specified function is
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index 0e0156ac6b..54999fa163 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -322,7 +322,6 @@ notation meaning equivalent decimal value(s) ~
<Bar> vertical bar | 124 *<Bar>*
<Del> delete 127
<CSI> command sequence intro ALT-Esc 155 *<CSI>*
-<xCSI> CSI when typed in the GUI *<xCSI>*
<EOL> end-of-line (can be <CR>, <NL> or <CR><NL>,
depends on system and 'fileformat') *<EOL>*
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index bb42a87034..f6fcbe8fb9 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -749,8 +749,8 @@ omnifunc({findstart}, {base}) *vim.lsp.omnifunc()*
set_log_level({level}) *vim.lsp.set_log_level()*
Sets the global log level for LSP logging.
- Levels by name: "trace", "debug", "info", "warn", "error"
- Level numbers begin with "trace" at 0
+ Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
+ Level numbers begin with "TRACE" at 0
Use `lsp.log_levels` for reverse lookup.
@@ -887,10 +887,10 @@ start_client({config}) *vim.lsp.start_client()*
default true): Allow using
incremental sync for buffer edits
• debounce_text_changes (number,
- default 150): Debounce didChange
+ default nil): Debounce didChange
notifications to the server by the
given number in milliseconds. No
- debounce occurs if set to 0.
+ debounce occurs if nil
• exit_timeout (number, default 500):
Milliseconds to wait for server to
exit cleanly after sending the
@@ -1008,7 +1008,7 @@ document_highlight() *vim.lsp.buf.document_highlight()*
Send request to the server to resolve document highlights for
the current text document position. This request can be
triggered by a key mapping or by events such as `CursorHold` ,
- eg:
+ e.g.:
>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
@@ -1024,11 +1024,12 @@ document_symbol() *vim.lsp.buf.document_symbol()*
Lists all symbols in the current buffer in the quickfix
window.
-execute_command({command}) *vim.lsp.buf.execute_command()*
+execute_command({command_params}) *vim.lsp.buf.execute_command()*
Executes an LSP server command.
Parameters: ~
- {command} A valid `ExecuteCommandParams` object
+ {command_params} table A valid `ExecuteCommandParams`
+ object
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
@@ -1331,7 +1332,7 @@ signature_help({_}, {result}, {ctx}, {config})
Lua module: vim.lsp.util *lsp-util*
*vim.lsp.util.apply_text_document_edit()*
-apply_text_document_edit({text_document_edit}, {index})
+apply_text_document_edit({text_document_edit}, {index}, {offset_encoding})
Applies a `TextDocumentEdit` , which is a list of changes to a
single document.
@@ -1351,18 +1352,19 @@ apply_text_edits({text_edits}, {bufnr}, {offset_encoding})
Parameters: ~
{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`
+ {offset_encoding} string utf-8|utf-16|utf-32 defaults to
+ encoding of first client of `bufnr`
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
*vim.lsp.util.apply_workspace_edit()*
-apply_workspace_edit({workspace_edit})
+apply_workspace_edit({workspace_edit}, {offset_encoding})
Applies a `WorkspaceEdit` .
Parameters: ~
- {workspace_edit} (table) `WorkspaceEdit`
+ {workspace_edit} table `WorkspaceEdit`
+ {offset_encoding} string utf-8|utf-16|utf-32 (required)
buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()*
Removes document highlights from a buffer.
@@ -1379,9 +1381,7 @@ buf_highlight_references({bufnr}, {references}, {offset_encoding})
{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`
+ "utf-32".
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
@@ -1470,16 +1470,19 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
See also: ~
|softtabstop|
-jump_to_location({location}) *vim.lsp.util.jump_to_location()*
+ *vim.lsp.util.jump_to_location()*
+jump_to_location({location}, {offset_encoding})
Jumps to a location.
Parameters: ~
- {location} ( `Location` | `LocationLink` )
+ {location} table ( `Location` | `LocationLink` )
+ {offset_encoding} string utf-8|utf-16|utf-32 (required)
Return: ~
`true` if the jump succeeded
-locations_to_items({locations}) *vim.lsp.util.locations_to_items()*
+ *vim.lsp.util.locations_to_items()*
+locations_to_items({locations}, {offset_encoding})
Returns the items with the byte position calculated correctly
and in sorted order, for display in quickfix and location
lists.
@@ -1488,8 +1491,10 @@ locations_to_items({locations}) *vim.lsp.util.locations_to_items()*
|setqflist()| or |setloclist()|.
Parameters: ~
- {locations} (table) list of `Location` s or
- `LocationLink` s
+ {locations} table list of `Location` s or
+ `LocationLink` s
+ {offset_encoding} string offset_encoding for locations
+ utf-8|utf-16|utf-32
Return: ~
(table) list of items
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 80c1f58323..3d4abed550 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1872,6 +1872,15 @@ add({filetypes}) *vim.filetype.add()*
{filetypes} table A table containing new filetype maps
(see example).
+match({name}, {bufnr}) *vim.filetype.match()*
+ Set the filetype for the given buffer from a file name.
+
+ Parameters: ~
+ {name} string File name (can be an absolute or relative
+ path)
+ {bufnr} number|nil The buffer to set the filetype for.
+ Defaults to the current buffer.
+
==============================================================================
Lua module: keymap *lua-keymap*
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index f6d9e45d64..9244638788 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -82,8 +82,7 @@ modes.
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.
+ mapping. This is for when an abbreviation applied.
Note: Trailing spaces are included in the {lhs}. This
unmap does NOT work: >
:map @@ foo
@@ -245,7 +244,7 @@ go through the main loop (e.g. to update the display), return "\<Ignore>".
This is similar to "nothing" but makes Vim return from the loop that waits for
input.
-Also, keep in mind that the expression may be evaluated when looking for
+Keep in mind that the expression may be evaluated when looking for
typeahead, before the previous command has been executed. For example: >
func StoreColumn()
let g:column = col('.')
@@ -1218,7 +1217,7 @@ scripts.
*:command-verbose*
When 'verbose' is non-zero, listing a command will also display where it was
-last defined. Example: >
+last defined and any completion argument. Example: >
:verbose command TOhtml
< Name Args Range Complete Definition ~
@@ -1333,6 +1332,8 @@ completion can be enabled:
-complete=custom,{func} custom completion, defined via {func}
-complete=customlist,{func} custom completion, defined via {func}
+If you specify completion while there is nothing to complete (-nargs=0, the
+default) then you get error *E1208* .
Note: That some completion methods might expand environment variables.
@@ -1435,6 +1436,9 @@ There are some special cases as well:
-register The first argument to the command can be an optional
register name (like :del, :put, :yank).
-buffer The command will only be available in the current buffer.
+ -keepscript Do not use the location of where the user command was
+ defined for verbose messages, use the location of where
+ the user command was invoked.
In the cases of the -count and -register attributes, if the optional argument
is supplied, it is removed from the argument list and is available to the
diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt
index 3bbf36c642..2aa49cee1e 100644
--- a/runtime/doc/mbyte.txt
+++ b/runtime/doc/mbyte.txt
@@ -489,8 +489,8 @@ Use the RPM or port for your system.
window specific to the input method.
-USING XIM *multibyte-input* *E284* *E286* *E287* *E288*
- *E285* *E289*
+USING XIM *multibyte-input* *E284* *E285* *E286* *E287*
+ *E288* *E289*
Note that Display and Input are independent. It is possible to see your
language even though you have no input method for it. But when your Display
diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt
index 6fbd9ec922..950028d9cc 100644
--- a/runtime/doc/message.txt
+++ b/runtime/doc/message.txt
@@ -112,7 +112,8 @@ wiped out a buffer which contains a mark or is referenced in another way.
*E95* >
Buffer with this name already exists
-You cannot have two buffers with the same name.
+You cannot have two buffers with exactly the same name. This includes the
+path leading to the file.
*E72* >
Close error on swap file
@@ -513,10 +514,10 @@ If you type "gq", it will execute this mapping, which will call "gq" again.
*E22* >
Scripts nested too deep
-Scripts can be read with the "-s" command-line argument and with the ":source"
-command. The script can then again read another script. This can continue
-for about 14 levels. When more nesting is done, Vim assumes that there is a
-recursive loop somewhere and stops with this error message.
+Scripts can be read with the "-s" command-line argument and with the
+`:source!` command. The script can then again read another script. This can
+continue for about 14 levels. When more nesting is done, Vim assumes that
+there is a recursive loop and stops with this error message.
*E300* >
Swap file already exists (symlink attack?)
@@ -686,6 +687,7 @@ Ex command or function was given an invalid argument. Or |jobstart()| or
Trailing characters
An argument was given to an Ex command that does not permit one.
+Or the argument has invalid characters and has not been recognized.
*E477* *E478* >
No ! allowed
diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt
index bfacbe19f5..f322764ecf 100644
--- a/runtime/doc/nvim_terminal_emulator.txt
+++ b/runtime/doc/nvim_terminal_emulator.txt
@@ -162,12 +162,11 @@ command name, for example: >
This opens two windows:
gdb window A terminal window in which "gdb vim" is executed. Here you
- can directly interact with gdb. The buffer name is "!gdb".
+ can directly interact with gdb.
program window A terminal window for the executed program. When "run" is
used in gdb the program I/O will happen in this window, so
- that it does not interfere with controlling gdb. The buffer
- name is "gdb program".
+ that it does not interfere with controlling gdb.
The current window is used to show the source code. When gdb pauses the
source file location will be displayed, if possible. A sign is used to
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index c929178f5a..13a19d8991 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -20,9 +20,13 @@ achieve special effects. These options come in three forms:
1. Setting options *set-option* *E764*
*:se* *:set*
-:se[t] Show all options that differ from their default value.
+:se[t][!] Show all options that differ from their default value.
+ When [!] is present every option is on a separate
+ line.
-:se[t] all Show all options.
+:se[t][!] all Show all options.
+ When [!] is present every option is on a separate
+ line.
*E518* *E519*
:se[t] {option}? Show value of {option}.
@@ -235,7 +239,7 @@ happens when the buffer is not loaded, but they are lost when the buffer is
wiped out |:bwipe|.
*:setl* *:setlocal*
-:setl[ocal] ... Like ":set" but set only the value local to the
+:setl[ocal][!] ... Like ":set" but set only the value local to the
current buffer or window. Not all options have a
local value. If the option does not have a local
value the global value is set.
@@ -257,7 +261,7 @@ wiped out |:bwipe|.
{option}, so that the global value will be used.
*:setg* *:setglobal*
-:setg[lobal] ... Like ":set" but set only the global value for a local
+:setg[lobal][!] ... Like ":set" but set only the global value for a local
option without changing the local value.
When displaying an option, the global value is shown.
With the "all" argument: display global values for all
@@ -304,7 +308,7 @@ value to the local value, it doesn't switch back to using the global value
This will make the local value of 'path' empty, so that the global value is
used. Thus it does the same as: >
:setlocal path=
-Note: In the future more global options can be made global-local. Using
+Note: In the future more global options can be made |global-local|. Using
":setlocal" on a global option might work differently then.
@@ -686,10 +690,10 @@ A jump table for the options with a short description can be found at |Q_op|.
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
+ 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
+ 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
@@ -5131,7 +5135,8 @@ A jump table for the options with a short description can be found at |Q_op|.
Don't include both "curdir" and "sesdir". When neither is included
filenames are stored as absolute paths.
-
+ If you leave out "options" many things won't work well after restoring
+ the session.
*'shada'* *'sd'* *E526* *E527* *E528*
'shada' 'sd' string (Vim default for
Win32: !,'100,<50,s10,h,rA:,rB:
@@ -6781,12 +6786,16 @@ A jump table for the options with a short description can be found at |Q_op|.
*'virtualedit'* *'ve'*
'virtualedit' 've' string (default "")
- global
+ global or local to window |global-local|
A comma separated list of these words:
block Allow virtual editing in Visual block mode.
insert Allow virtual editing in Insert mode.
all Allow virtual editing in all modes.
onemore Allow the cursor to move just past the end of the line
+ none When used as the local value, do not allow virtual
+ editing even when the global value is set. When used
+ as the global value, "none" is the same as "".
+ NONE Alternative spelling of "none".
Virtual editing means that the cursor can be positioned where there is
no actual character. This can be halfway into a tab or beyond the end
diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt
index dfed39dba6..634145da3e 100644
--- a/runtime/doc/pattern.txt
+++ b/runtime/doc/pattern.txt
@@ -304,7 +304,7 @@ the pattern.
==============================================================================
2. The definition of a pattern *search-pattern* *pattern* *[pattern]*
*regular-expression* *regexp* *Pattern*
- *E76* *E383* *E476*
+ *E383* *E476*
For starters, read chapter 27 of the user manual |usr_27.txt|.
@@ -1036,6 +1036,8 @@ match ASCII characters, as indicated by the range.
\(\) A pattern enclosed by escaped parentheses. */\(* */\(\)* */\)*
E.g., "\(^a\)" matches 'a' at the start of a line.
+ There can only be ten of these. You can use "\%(" to add more, but
+ not counting it as a sub-expression.
*E51* *E54* *E55* *E872* *E873*
\1 Matches the same string that was matched by */\1* *E65*
@@ -1058,7 +1060,7 @@ x A single character, with no special meaning, matches itself
\x A backslash followed by a single character, with no special meaning,
is reserved for future expansions
-[] (with 'nomagic': \[]) */[]* */\[]* */\_[]* */collection*
+[] (with 'nomagic': \[]) */[]* */\[]* */\_[]* */collection* *E76*
\_[]
A collection. This is a sequence of characters enclosed in square
brackets. It matches any single character in the collection.
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index ba1da209f7..873fbd8971 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -232,7 +232,7 @@ processing a quickfix or location list command, it will be aborted.
":qall!" |:qall|, except that Nvim exits non-zero or
[count].
- *:cf* *:cfile*
+ *:cf* *:cfi* *:cfile*
:cf[ile][!] [errorfile] Read the error file and jump to the first error.
This is done automatically when Vim is started with
the -q option. You can use this command when you
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index bb775ec884..978142a1e0 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -188,7 +188,7 @@ argument.
changes and writing.
-e *-e* *-E*
--E Start Nvim in Ex mode |gQ|.
+-E Start Nvim in Ex mode |gQ|, see |Ex-mode|.
If stdin is not a TTY:
-e reads/executes stdin as Ex commands.
@@ -409,7 +409,12 @@ accordingly, proceeding as follows:
4. Setup |default-mappings| and |default-autocmds|.
-5. Load user config (execute Ex commands from files, environment, …).
+5. Enable filetype and indent plugins.
+ This does the same as the command: >
+ :runtime! ftplugin.vim indent.vim
+< Skipped if the "-u NONE" command line argument was given.
+
+6. Load user config (execute Ex commands from files, environment, …).
$VIMINIT environment variable is read as one Ex command line (separate
multiple commands with '|' or <NL>).
*config* *init.vim* *init.lua* *vimrc* *exrc*
@@ -453,21 +458,19 @@ accordingly, proceeding as follows:
- The file ".nvimrc"
- The file ".exrc"
-6. Enable filetype and indent plugins.
- This does the same as the commands: >
- :runtime! filetype.vim
- :runtime! ftplugin.vim
- :runtime! indent.vim
-< Skipped if ":filetype … off" was called or if the "-u NONE" command
- line argument was given.
+7. Enable filetype detection.
+ This does the same as the command: >
+ :runtime! filetype.lua filetype.vim
+< Skipped if ":filetype off" was called or if the "-u NONE" command line
+ argument was given.
-7. Enable syntax highlighting.
+8. Enable syntax highlighting.
This does the same as the command: >
:runtime! syntax/syntax.vim
< Skipped if ":syntax off" was called or if the "-u NONE" command
line argument was given.
-8. Load the plugin scripts. *load-plugins*
+9. Load the plugin scripts. *load-plugins*
This does the same as the command: >
:runtime! plugin/**/*.vim
:runtime! plugin/**/*.lua
@@ -497,21 +500,21 @@ accordingly, proceeding as follows:
if packages have been found, but that should not add a directory
ending in "after".
-9. Set 'shellpipe' and 'shellredir'
+10. Set 'shellpipe' and 'shellredir'
The 'shellpipe' and 'shellredir' options are set according to the
value of the 'shell' option, unless they have been set before.
This means that Nvim will figure out the values of 'shellpipe' and
'shellredir' for you, unless you have set them yourself.
-10. Set 'updatecount' to zero, if "-n" command argument used
+11. Set 'updatecount' to zero, if "-n" command argument used
-11. Set binary options if the |-b| flag was given.
+12. Set binary options if the |-b| flag was given.
-12. Read the |shada-file|.
+13. Read the |shada-file|.
-13. Read the quickfix file if the |-q| flag was given, or exit on failure.
+14. Read the quickfix file if the |-q| flag was given, or exit on failure.
-14. Open all windows
+15. Open all windows
When the |-o| flag was given, windows will be opened (but not
displayed yet).
When the |-p| flag was given, tab pages will be created (but not
@@ -521,7 +524,7 @@ accordingly, proceeding as follows:
Buffers for all windows will be loaded, without triggering |BufAdd|
autocommands.
-15. Execute startup commands
+16. Execute startup commands
If a |-t| flag was given, the tag is jumped to.
Commands given with |-c| and |+cmd| are executed.
If the 'insertmode' option is set, Insert mode is entered.
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 0fd481cd83..be1586ab41 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -920,12 +920,16 @@ in .../after/syntax/baan.vim (see |after-directory|). Eg: >
BASIC *basic.vim* *vb.vim* *ft-basic-syntax* *ft-vb-syntax*
-Both Visual Basic and "normal" basic use the extension ".bas". To detect
+Both Visual Basic and "normal" BASIC use the extension ".bas". To detect
which one should be used, Vim checks for the string "VB_Name" in the first
five lines of the file. If it is not found, filetype will be "basic",
otherwise "vb". Files with the ".frm" extension will always be seen as Visual
Basic.
+If the automatic detection doesn't work for you or you only edit, for
+example, FreeBASIC files, use this in your startup vimrc: >
+ :let filetype_bas = "freebasic"
+
C *c.vim* *ft-c-syntax*
@@ -1406,7 +1410,7 @@ add the following line to your startup file: >
:let g:filetype_euphoria = "euphoria4"
-Elixir and Euphoria share the *.ex file extension. If the filetype is
+Elixir and Euphoria share the *.ex file extension. If the filetype is
specifically set as Euphoria with the g:filetype_euphoria variable, or the
file is determined to be Euphoria based on keywords in the file, then the
filetype will be set as Euphoria. Otherwise, the filetype will default to
@@ -1437,7 +1441,7 @@ The following file extensions are auto-detected as Elixir file types:
*.ex, *.exs, *.eex, *.leex, *.lock
-Elixir and Euphoria share the *.ex file extension. If the filetype is
+Elixir and Euphoria share the *.ex file extension. If the filetype is
specifically set as Euphoria with the g:filetype_euphoria variable, or the
file is determined to be Euphoria based on keywords in the file, then the
filetype will be set as Euphoria. Otherwise, the filetype will default to
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 7de6a0f890..5829dbdd6b 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -467,8 +467,9 @@ parse_query({lang}, {query}) *parse_query()*
• `info.patterns` contains information about predicates.
Parameters: ~
- {lang} The language
- {query} A string containing the query (s-expr syntax)
+ {lang} string The language
+ {query} string A string containing the query (s-expr
+ syntax)
Return: ~
The query
@@ -665,7 +666,7 @@ LanguageTree:invalidate({self}, {reload}) *LanguageTree:invalidate()*
{self}
LanguageTree:is_valid({self}) *LanguageTree:is_valid()*
- Determines whether this tree is valid. If the tree is invalid, `parse()` must be called to get the an updated tree.
+ Determines whether this tree is valid. If the tree is invalid, `parse()` must be called to get the updated tree.
Parameters: ~
{self}
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 8a4468a130..fc0230c62d 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -249,7 +249,7 @@ g8 Print the hex values of the bytes used in the
To enter |Terminal-mode| automatically: >
autocmd TermOpen * startinsert
<
- *:!cmd* *:!* *E34*
+ *:!cmd* *:!*
:!{cmd} Execute {cmd} with 'shell'. See also |:terminal|.
The command runs in a non-interactive shell connected
@@ -261,6 +261,7 @@ g8 Print the hex values of the bytes used in the
Use |jobstart()| instead. >
:call jobstart('foo', {'detach':1})
<
+ *E34*
Any "!" in {cmd} is replaced with the previous
external command (see also 'cpoptions'), unless
escaped by a backslash. Example: ":!ls" followed by
@@ -357,19 +358,19 @@ g8 Print the hex values of the bytes used in the
:redi[r] END End redirecting messages.
*:filt* *:filter*
-:filt[er][!] {pat} {command}
-:filt[er][!] /{pat}/ {command}
+:filt[er][!] {pattern} {command}
+:filt[er][!] /{pattern}/ {command}
Restrict the output of {command} to lines matching
- with {pat}. For example, to list only xml files: >
+ with {pattern}. For example, to list only xml files: >
:filter /\.xml$/ oldfiles
< If the [!] is given, restrict the output of {command}
- to lines that do NOT match {pat}.
+ to lines that do NOT match {pattern}.
- {pat} is a Vim search pattern. Instead of enclosing
+ {pattern} is a Vim search pattern. Instead of enclosing
it in / any non-ID character (see |'isident'|) can be
- used, so long as it does not appear in {pat}. Without
- the enclosing character the pattern cannot include the
- bar character. 'ignorecase' is not used.
+ used, so long as it does not appear in {pattern}.
+ Without the enclosing character the pattern cannot
+ include the bar character. 'ignorecase' is not used.
The pattern is matched against the relevant part of
the output, not necessarily the whole line. Only some
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 4fcaf15717..7e61eac404 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -23,8 +23,10 @@ centralized reference of the differences.
==============================================================================
2. Defaults *nvim-defaults*
-- Syntax highlighting is enabled by default
-- ":filetype plugin indent on" is enabled by default
+- Filetype detection is enabled by default. This can be disabled by adding
+ ":filetype off" to |init.vim|.
+- Syntax highlighting is enabled by default. This can be disabled by adding
+ ":syntax off" to |init.vim|.
- 'autoindent' is enabled
- 'autoread' is enabled
diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt
index 4a69fc989b..5563a56216 100644
--- a/runtime/doc/visual.txt
+++ b/runtime/doc/visual.txt
@@ -478,6 +478,10 @@ Commands in Select mode:
- ESC stops Select mode.
- CTRL-O switches to Visual mode for the duration of one command. *v_CTRL-O*
- CTRL-G switches to Visual mode.
+- CTRL-R {register} selects the register to be used for the text that is
+ deleted when typing text. *v_CTRL-R*
+ Unless you specify the "_" (black hole) register, the unnamed register is
+ also overwritten.
Otherwise, typed characters are handled as in Visual mode.
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index bb31895c96..5b91321c40 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -124,7 +124,7 @@ highlight group (|hl-EndOfBuffer|) can be used to change the highlighting of
the filler characters.
==============================================================================
-3. Opening and closing a window *opening-window* *E36*
+3. Opening and closing a window *opening-window*
CTRL-W s *CTRL-W_s*
CTRL-W S *CTRL-W_S*
@@ -223,6 +223,10 @@ CTRL-W ge *CTRL-W_ge*
Note that the 'splitbelow' and 'splitright' options influence where a new
window will appear.
+ *E36*
+Creating a window will fail if there is not enough room. Every window needs
+at least one screen line and column, sometimes more. Options 'winminheight'
+and 'winminwidth' are relevant.
*:vert* *:vertical*
:vert[ical] {cmd}
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 7fdbfe32d8..6a27998cf4 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 Dec 27
+" Last Change: 2022 Jan 23
" Listen very carefully, I will say this only once
if exists("did_load_filetypes")
@@ -189,7 +189,8 @@ au BufNewFile,BufRead *.awk,*.gawk setf awk
au BufNewFile,BufRead *.mch,*.ref,*.imp setf b
" BASIC or Visual Basic
-au BufNewFile,BufRead *.bas call dist#ft#FTVB("basic")
+au BufNewFile,BufRead *.bas call dist#ft#FTbas()
+au BufNewFile,BufRead *.bi,*.bm call dist#ft#FTbas()
" Visual Basic Script (close to Visual Basic) or Visual Basic .NET
au BufNewFile,BufRead *.vb,*.vbs,*.dsm,*.ctl setf vb
@@ -198,7 +199,7 @@ au BufNewFile,BufRead *.vb,*.vbs,*.dsm,*.ctl setf vb
au BufNewFile,BufRead *.iba,*.ibi setf ibasic
" FreeBasic file (similar to QBasic)
-au BufNewFile,BufRead *.fb,*.bi setf freebasic
+au BufNewFile,BufRead *.fb setf freebasic
" Batch file for MSDOS.
au BufNewFile,BufRead *.bat,*.sys setf dosbatch
@@ -493,7 +494,7 @@ au BufNewFile,BufRead */debian/patches/* call dist#ft#Dep3patch()
" Diff files
au BufNewFile,BufRead *.diff,*.rej setf diff
au BufNewFile,BufRead *.patch
- \ if getline(1) =~ '^From [0-9a-f]\{40\} Mon Sep 17 00:00:00 2001$' |
+ \ if getline(1) =~# '^From [0-9a-f]\{40,\} Mon Sep 17 00:00:00 2001$' |
\ setf gitsendemail |
\ else |
\ setf diff |
@@ -649,6 +650,9 @@ au BufNewFile,BufRead *.fsl setf framescript
" FStab
au BufNewFile,BufRead fstab,mtab setf fstab
+" Fusion
+au BufRead,BufNewFile *.fusion setf fusion
+
" F# or Forth
au BufNewFile,BufRead *.fs call dist#ft#FTfs()
@@ -661,6 +665,12 @@ au BufNewFile,BufRead .gdbinit,gdbinit setf gdb
" GDMO
au BufNewFile,BufRead *.mo,*.gdmo setf gdmo
+" GDscript
+au BufNewFile,BufRead *.gd setf gdscript
+
+" Godot resource
+au BufRead,BufNewFile *.tscn,*.tres setf gdresource
+
" Gedcom
au BufNewFile,BufRead *.ged,lltxxxxx.txt setf gedcom
@@ -672,26 +682,28 @@ autocmd BufRead,BufNewFile *.gift setf gift
" Git
au BufNewFile,BufRead COMMIT_EDITMSG,MERGE_MSG,TAG_EDITMSG setf gitcommit
-au BufNewFile,BufRead *.git/config,.gitconfig,/etc/gitconfig setf gitconfig
+au BufNewFile,BufRead NOTES_EDITMSG,EDIT_DESCRIPTION setf gitcommit
+au BufNewFile,BufRead *.git/config,.gitconfig,*/etc/gitconfig setf gitconfig
au BufNewFile,BufRead */.config/git/config setf gitconfig
+au BufNewFile,BufRead *.git/config.worktree setf gitconfig
+au BufNewFile,BufRead *.git/worktrees/*/config.worktree setf gitconfig
au BufNewFile,BufRead .gitmodules,*.git/modules/*/config setf gitconfig
if !empty($XDG_CONFIG_HOME)
au BufNewFile,BufRead $XDG_CONFIG_HOME/git/config setf gitconfig
endif
au BufNewFile,BufRead git-rebase-todo setf gitrebase
au BufRead,BufNewFile .gitsendemail.msg.?????? setf gitsendemail
-au BufNewFile,BufRead .msg.[0-9]*
- \ if getline(1) =~ '^From.*# This line is ignored.$' |
- \ setf gitsendemail |
- \ endif
au BufNewFile,BufRead *.git/*
- \ if getline(1) =~ '^\x\{40\}\>\|^ref: ' |
+ \ if getline(1) =~# '^\x\{40,\}\>\|^ref: ' |
\ setf git |
\ endif
" Gkrellmrc
au BufNewFile,BufRead gkrellmrc,gkrellmrc_? setf gkrellmrc
+" GLSL
+au BufNewFile,BufRead *.glsl setf glsl
+
" GP scripts (2.0 and onward)
au BufNewFile,BufRead *.gp,.gprc setf gp
@@ -712,15 +724,19 @@ au BufNewFile,BufRead gitolite.conf setf gitolite
au BufNewFile,BufRead {,.}gitolite.rc,example.gitolite.rc setf perl
" Gnuplot scripts
-au BufNewFile,BufRead *.gpi setf gnuplot
+au BufNewFile,BufRead *.gpi,.gnuplot setf gnuplot
" Go (Google)
au BufNewFile,BufRead *.go setf go
au BufNewFile,BufRead Gopkg.lock setf toml
+au BufRead,BufNewFile go.work setf gowork
" GrADS scripts
au BufNewFile,BufRead *.gs setf grads
+" GraphQL
+au BufNewFile,BufRead *.graphql,*.graphqls,*.gql setf graphql
+
" Gretl
au BufNewFile,BufRead *.gretl setf gretl
@@ -736,12 +752,18 @@ au BufNewFile,BufRead */etc/group,*/etc/group-,*/etc/group.edit,*/etc/gshadow,*/
" GTK RC
au BufNewFile,BufRead .gtkrc,gtkrc setf gtkrc
+" Hack
+au BufRead,BufNewFile *.hack,*.hackpartial setf hack
+
" Haml
au BufNewFile,BufRead *.haml setf haml
" Hamster Classic | Playground files
au BufNewFile,BufRead *.hsm setf hamster
+" Handlebars
+au BufNewFile,BufRead *.hbs setf handlebars
+
" Haskell
au BufNewFile,BufRead *.hs,*.hsc,*.hs-boot,*.hsig setf haskell
au BufNewFile,BufRead *.lhs setf lhaskell
@@ -754,12 +776,21 @@ au BufNewFile,BufRead cabal.config setf cabalconfig
au BufNewFile,BufRead *.ht setf haste
au BufNewFile,BufRead *.htpp setf hastepreproc
+" HCL
+au BufRead,BufNewFile *.hcl setf hcl
+
" Hercules
au BufNewFile,BufRead *.vc,*.ev,*.sum,*.errsum setf hercules
+" HEEx
+au BufRead,BufNewFile *.heex setf heex
+
" HEX (Intel)
au BufNewFile,BufRead *.hex,*.h32 setf hex
+" Hjson
+au BufNewFile,BufRead *.hjson setf hjson
+
" Hollywood
au BufRead,BufNewFile *.hws setf hollywood
@@ -884,6 +915,9 @@ au BufNewFile,BufRead *.jov,*.j73,*.jovial setf jovial
" JSON
au BufNewFile,BufRead *.json,*.jsonp,*.webmanifest setf json
+" JSON5
+au BufNewFile,BufRead *.json5 setf json5
+
" JSON Patch (RFC 6902)
au BufNewFile,BufRead *.json-patch setf json
@@ -935,6 +969,9 @@ au BufNewFile,BufRead *.ldif setf ldif
" Ld loader
au BufNewFile,BufRead *.ld setf ld
+" Ledger
+au BufRead,BufNewFile *.ldg,*.ledger,*.journal setf ledger
+
" Less
au BufNewFile,BufRead *.less setf less
@@ -1172,6 +1209,9 @@ au BufNewFile,BufRead *.nginx,nginx*.conf,*nginx.conf,*/etc/nginx/*,*/usr/local/
" Ninja file
au BufNewFile,BufRead *.ninja setf ninja
+" Nix
+au BufRead,BufNewFile *.nix setf nix
+
" NPM RC file
au BufNewFile,BufRead npmrc,.npmrc setf dosini
@@ -1213,6 +1253,9 @@ au BufNewFile,BufRead *.xom,*.xin setf omnimark
" OPAM
au BufNewFile,BufRead opam,*.opam,*.opam.template setf opam
+" OpenFOAM
+au BufNewFile,BufRead [a-zA-Z0-9]*Dict\(.*\)\=,[a-zA-Z]*Properties\(.*\)\=,*Transport\(.*\),fvSchemes,fvSolution,fvConstrains,fvModels,*/constant/g,*/0\(\.orig\)\=/* call dist#ft#FTfoam()
+
" OpenROAD
au BufNewFile,BufRead *.or setf openroad
@@ -1354,6 +1397,9 @@ au BufNewFile,BufRead *printcap
au BufNewFile,BufRead *termcap
\ let b:ptcap_type = "term" | setf ptcap
+" Prisma
+au BufRead,BufNewFile *.prisma setf prisma
+
" PCCTS / ANTLR
"au BufNewFile,BufRead *.g setf antlr
au BufNewFile,BufRead *.g setf pccts
@@ -1361,6 +1407,9 @@ au BufNewFile,BufRead *.g setf pccts
" PPWizard
au BufNewFile,BufRead *.it,*.ih setf ppwiz
+" Pug
+au BufRead,BufNewFile *.pug setf pug
+
" Puppet
au BufNewFile,BufRead Puppetfile setf ruby
@@ -1426,6 +1475,9 @@ au BufNewFile,BufRead *.pyx,*.pxd setf pyrex
au BufNewFile,BufRead *.py,*.pyw,.pythonstartup,.pythonrc setf python
au BufNewFile,BufRead *.ptl,*.pyi,SConstruct setf python
+" QL
+au BufRead,BufNewFile *.ql,*.qll setf ql
+
" Radiance
au BufNewFile,BufRead *.rad,*.mat setf radiance
@@ -1830,6 +1882,9 @@ au BufNewFile,BufRead */etc/sudoers,sudoers.tmp setf sudoers
" SVG (Scalable Vector Graphics)
au BufNewFile,BufRead *.svg setf svg
+" Surface
+au BufRead,BufNewFile *.sface setf surface
+
" Tads (or Nroff or Perl test file)
au BufNewFile,BufRead *.t
\ if !dist#ft#FTnroff() && !dist#ft#FTperl() | setf tads | endif
@@ -1847,6 +1902,9 @@ au BufRead,BufNewFile *.task setf taskedit
" Tcl (JACL too)
au BufNewFile,BufRead *.tcl,*.tm,*.tk,*.itcl,*.itk,*.jacl,.tclshrc,.wishrc setf tcl
+" Teal
+au BufRead,BufNewFile *.tl setf teal
+
" TealInfo
au BufNewFile,BufRead *.tli setf tli
@@ -1864,6 +1922,9 @@ au BufRead,BufNewFile *.ttl
" Terminfo
au BufNewFile,BufRead *.ti setf terminfo
+" Terraform
+au BufRead,BufNewFile *.tfvars setf terraform
+
" TeX
au BufNewFile,BufRead *.latex,*.sty,*.dtx,*.ltx,*.bbl setf tex
au BufNewFile,BufRead *.tex call dist#ft#FTtex()
@@ -1881,7 +1942,13 @@ au BufNewFile,BufRead texmf.cnf setf texmf
au BufNewFile,BufRead .tidyrc,tidyrc,tidy.conf setf tidy
" TF mud client
-au BufNewFile,BufRead *.tf,.tfrc,tfrc setf tf
+au BufNewFile,BufRead .tfrc,tfrc setf tf
+
+" TF mud client or terraform
+au BufNewFile,BufRead *.tf call dist#ft#FTtf()
+
+" TLA+
+au BufNewFile,BufRead *.tla setf tla
" tmux configuration
au BufNewFile,BufRead {.,}tmux*.conf setf tmux
@@ -1890,7 +1957,7 @@ au BufNewFile,BufRead {.,}tmux*.conf setf tmux
au BufNewFile,BufRead *.toml setf toml
" TPP - Text Presentation Program
-au BufNewFile,BufReadPost *.tpp setf tpp
+au BufNewFile,BufRead *.tpp setf tpp
" Treetop
au BufRead,BufNewFile *.treetop setf treetop
@@ -2144,6 +2211,9 @@ au BufNewFile,BufRead *.raml setf raml
" yum conf (close enough to dosini)
au BufNewFile,BufRead */etc/yum.conf setf dosini
+" YANG
+au BufRead,BufNewFile *.yang setf yang
+
" Zimbu
au BufNewFile,BufRead *.zu setf zimbu
" Zimbu Templates
@@ -2285,6 +2355,9 @@ au BufNewFile,BufRead Kconfig.* call s:StarSetf('kconfig')
" Lilo: Linux loader
au BufNewFile,BufRead lilo.conf* call s:StarSetf('lilo')
+" Libsensors
+au BufNewFile,BufRead */etc/sensors.d/[^.]* call s:StarSetf('sensors')
+
" Logcheck
au BufNewFile,BufRead */etc/logcheck/*.d*/* call s:StarSetf('logcheck')
diff --git a/runtime/ftplugin/git.vim b/runtime/ftplugin/git.vim
deleted file mode 100644
index 75b20f021e..0000000000
--- a/runtime/ftplugin/git.vim
+++ /dev/null
@@ -1,41 +0,0 @@
-" Vim filetype plugin
-" Language: generic git output
-" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2019 Dec 05
-
-" Only do this when not done yet for this buffer
-if (exists("b:did_ftplugin"))
- finish
-endif
-let b:did_ftplugin = 1
-
-if !exists('b:git_dir')
- if expand('%:p') =~# '[\/]\.git[\/]modules[\/]\|:[\/][\/]\|^\a\a\+:'
- " Stay out of the way
- elseif expand('%:p') =~# '[\/]\.git[\/]worktrees'
- let b:git_dir = matchstr(expand('%:p'),'.*\.git[\/]worktrees[\/][^\/]\+\>')
- elseif expand('%:p') =~# '\.git\>'
- let b:git_dir = matchstr(expand('%:p'),'.*\.git\>')
- elseif $GIT_DIR != ''
- let b:git_dir = $GIT_DIR
- endif
- if (has('win32') || has('win64')) && exists('b:git_dir')
- let b:git_dir = substitute(b:git_dir,'\\','/','g')
- endif
-endif
-
-if exists('*shellescape') && exists('b:git_dir') && b:git_dir != ''
- if b:git_dir =~# '/\.git$' " Not a bare repository
- let &l:path = escape(fnamemodify(b:git_dir,':h'),'\, ').','.&l:path
- endif
- let &l:path = escape(b:git_dir,'\, ').','.&l:path
- let &l:keywordprg = 'git --git-dir='.shellescape(b:git_dir).' show'
-else
- setlocal keywordprg=git\ show
-endif
-if has('gui_running')
- let &l:keywordprg = substitute(&l:keywordprg,'^git\>','git --no-pager','')
-endif
-
-setlocal includeexpr=substitute(v:fname,'^[^/]\\+/','','')
-let b:undo_ftplugin = "setl keywordprg< path< includeexpr<"
diff --git a/runtime/ftplugin/gitcommit.vim b/runtime/ftplugin/gitcommit.vim
index 9b1998acaa..9342799b56 100644
--- a/runtime/ftplugin/gitcommit.vim
+++ b/runtime/ftplugin/gitcommit.vim
@@ -1,66 +1,57 @@
" Vim filetype plugin
" Language: git commit file
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2019 Dec 05
+" Last Change: 2022 Jan 05
" Only do this when not done yet for this buffer
if (exists("b:did_ftplugin"))
finish
endif
-runtime! ftplugin/git.vim
let b:did_ftplugin = 1
-setlocal comments=:# commentstring=#\ %s
setlocal nomodeline tabstop=8 formatoptions+=tl textwidth=72
setlocal formatoptions-=c formatoptions-=r formatoptions-=o formatoptions-=q formatoptions+=n
setlocal formatlistpat+=\\\|^\\s*[-*+]\\s\\+
+setlocal include=^+++
+setlocal includeexpr=substitute(v:fname,'^[bi]/','','')
-let b:undo_ftplugin = 'setl modeline< tabstop< formatoptions< tw< com< cms< formatlistpat<'
+let b:undo_ftplugin = 'setl modeline< tabstop< formatoptions< tw< com< cms< formatlistpat< inc< inex<'
-if exists("g:no_gitcommit_commands") || v:version < 700
- finish
-endif
+let s:l = search('\C\m^[#;@!$%^&|:] -\{24,\} >8 -\{24,\}$', 'cnW', '', 100)
+let &l:comments = ':' . (matchstr(getline(s:l ? s:l : '$'), '^[#;@!$%^&|:]\S\@!') . '#')[0]
+let &l:commentstring = &l:comments[1] . ' %s'
+unlet s:l
-if !exists("b:git_dir")
- let b:git_dir = expand("%:p:h")
+if exists("g:no_gitcommit_commands")
+ finish
endif
-command! -bang -bar -buffer -complete=custom,s:diffcomplete -nargs=* DiffGitCached :call s:gitdiffcached(<bang>0,b:git_dir,<f-args>)
+command! -bang -bar -buffer -complete=custom,s:diffcomplete -nargs=* DiffGitCached :call s:gitdiffcached(<bang>0, <f-args>)
let b:undo_ftplugin = b:undo_ftplugin . "|delc DiffGitCached"
-function! s:diffcomplete(A,L,P)
+function! s:diffcomplete(A, L, P) abort
let args = ""
if a:P <= match(a:L." -- "," -- ")+3
let args = args . "-p\n--stat\n--shortstat\n--summary\n--patch-with-stat\n--no-renames\n-B\n-M\n-C\n"
end
- if exists("b:git_dir") && a:A !~ '^-'
- let tree = fnamemodify(b:git_dir,':h')
- if strpart(getcwd(),0,strlen(tree)) == tree
- let args = args."\n".system("git diff --cached --name-only")
- endif
+ if a:A !~ '^-' && !empty(getftype('.git'))
+ let args = args."\n".system("git diff --cached --name-only")
endif
return args
endfunction
-function! s:gitdiffcached(bang,gitdir,...)
- let tree = fnamemodify(a:gitdir,':h')
+function! s:gitdiffcached(bang, ...) abort
let name = tempname()
- let git = "git"
- if strpart(getcwd(),0,strlen(tree)) != tree
- let git .= " --git-dir=".(exists("*shellescape") ? shellescape(a:gitdir) : '"'.a:gitdir.'"')
- endif
if a:0
- let extra = join(map(copy(a:000),exists("*shellescape") ? 'shellescape(v:val)' : "'\"'.v:val.'\"'"))
+ let extra = join(map(copy(a:000), 'shellescape(v:val)'))
else
let extra = "-p --stat=".&columns
endif
- call system(git." diff --cached --no-color --no-ext-diff ".extra." > ".(exists("*shellescape") ? shellescape(name) : name))
- exe "pedit ".(exists("*fnameescape") ? fnameescape(name) : name)
+ call system("git diff --cached --no-color --no-ext-diff ".extra." > ".shellescape(name))
+ exe "pedit " . fnameescape(name)
wincmd P
- let b:git_dir = a:gitdir
- command! -bang -bar -buffer -complete=custom,s:diffcomplete -nargs=* DiffGitCached :call s:gitdiffcached(<bang>0,b:git_dir,<f-args>)
- nnoremap <buffer> <silent> q :q<CR>
+ command! -bang -bar -buffer -complete=custom,s:diffcomplete -nargs=* DiffGitCached :call s:gitdiffcached(<bang>0, <f-args>)
setlocal buftype=nowrite nobuflisted noswapfile nomodifiable filetype=git
endfunction
diff --git a/runtime/ftplugin/gitrebase.vim b/runtime/ftplugin/gitrebase.vim
index 2fed53c829..143f86a251 100644
--- a/runtime/ftplugin/gitrebase.vim
+++ b/runtime/ftplugin/gitrebase.vim
@@ -1,22 +1,20 @@
" Vim filetype plugin
" Language: git rebase --interactive
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2019 Dec 05
+" Last Change: 2022 Jan 05
" Only do this when not done yet for this buffer
if (exists("b:did_ftplugin"))
finish
endif
-runtime! ftplugin/git.vim
let b:did_ftplugin = 1
-setlocal comments=:# commentstring=#\ %s formatoptions-=t
+let &l:comments = ':' . (matchstr(getline('$'), '^[#;@!$%^&|:]\S\@!') . '#')[0]
+let &l:commentstring = &l:comments[1] . ' %s'
+setlocal formatoptions-=t
setlocal nomodeline
-if !exists("b:undo_ftplugin")
- let b:undo_ftplugin = ""
-endif
-let b:undo_ftplugin = b:undo_ftplugin."|setl com< cms< fo< ml<"
+let b:undo_ftplugin = "setl com< cms< fo< ml<"
function! s:choose(word) abort
s/^\(\w\+\>\)\=\(\s*\)\ze\x\{4,40\}\>/\=(strlen(submatch(1)) == 1 ? a:word[0] : a:word) . substitute(submatch(2),'^$',' ','')/e
@@ -41,8 +39,7 @@ if exists("g:no_plugin_maps") || exists("g:no_gitrebase_maps")
finish
endif
-nnoremap <buffer> <expr> K col('.') < 7 && expand('<Lt>cword>') =~ '\X' && getline('.') =~ '^\w\+\s\+\x\+\>' ? 'wK' : 'K'
nnoremap <buffer> <silent> <C-A> :<C-U><C-R>=v:count1<CR>Cycle<CR>
nnoremap <buffer> <silent> <C-X> :<C-U><C-R>=v:count1<CR>Cycle!<CR>
-let b:undo_ftplugin = b:undo_ftplugin . "|exe 'nunmap <buffer> K'|exe 'nunmap <buffer> <C-A>'|exe 'nunmap <buffer> <C-X>'"
+let b:undo_ftplugin = b:undo_ftplugin . "|exe 'nunmap <buffer> <C-A>'|exe 'nunmap <buffer> <C-X>'"
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 417b661155..b4537c2882 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -30,7 +30,7 @@ M.handlers = setmetatable({}, {
__newindex = function(t, name, handler)
vim.validate { handler = {handler, "t" } }
rawset(t, name, handler)
- if not global_diagnostic_options[name] then
+ if global_diagnostic_options[name] == nil then
global_diagnostic_options[name] = true
end
end,
@@ -552,7 +552,8 @@ end
--- - `table`: Enable this feature with overrides. Use an empty table to use default values.
--- - `function`: Function with signature (namespace, bufnr) that returns any of the above.
---
----@param opts table Configuration table with the following keys:
+---@param opts table|nil When omitted or "nil", retrieve the current configuration. Otherwise, a
+--- configuration table with the following keys:
--- - underline: (default true) Use underline for diagnostics. Options:
--- * severity: Only underline diagnostics matching the given severity
--- |diagnostic-severity|
@@ -599,7 +600,7 @@ end
--- global diagnostic options.
function M.config(opts, namespace)
vim.validate {
- opts = { opts, 't' },
+ opts = { opts, 't', true },
namespace = { namespace, 'n', true },
}
@@ -611,10 +612,13 @@ function M.config(opts, namespace)
t = global_diagnostic_options
end
- for opt in pairs(global_diagnostic_options) do
- if opts[opt] ~= nil then
- t[opt] = opts[opt]
- end
+ if not opts then
+ -- Return current config
+ return vim.deepcopy(t)
+ end
+
+ for k, v in pairs(opts) do
+ t[k] = v
end
if namespace then
@@ -823,6 +827,7 @@ M.handlers.signs = {
}
bufnr = get_bufnr(bufnr)
+ opts = opts or {}
if opts.signs and opts.signs.severity then
diagnostics = filter_by_severity(opts.signs.severity, diagnostics)
@@ -890,6 +895,7 @@ M.handlers.underline = {
}
bufnr = get_bufnr(bufnr)
+ opts = opts or {}
if opts.underline and opts.underline.severity then
diagnostics = filter_by_severity(opts.underline.severity, diagnostics)
@@ -942,6 +948,7 @@ M.handlers.virtual_text = {
}
bufnr = get_bufnr(bufnr)
+ opts = opts or {}
local severity
if opts.virtual_text then
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 54b20f7391..7fa2fbe001 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -15,7 +15,7 @@ local function starsetf(ft)
end
end, {
-- Starset matches should always have lowest priority
- priority = -1,
+ priority = -math.huge,
}}
end
@@ -119,7 +119,13 @@ local extension = {
tcc = "cpp",
hxx = "cpp",
hpp = "cpp",
- cpp = function()
+ cpp = function(path, bufnr)
+ if vim.g.cynlib_syntax_for_cc then
+ return "cynlib"
+ end
+ return "cpp"
+ end,
+ cc = function(path, bufnr)
if vim.g.cynlib_syntax_for_cc then
return "cynlib"
end
@@ -214,26 +220,37 @@ local extension = {
["f08"] = "fortran",
fpc = "fpcmake",
fsl = "framescript",
- bi = "freebasic",
fb = "freebasic",
fsi = "fsharp",
fsx = "fsharp",
+ fusion = "fusion",
gdmo = "gdmo",
mo = "gdmo",
+ tres = "gdresource",
+ tscn = "gdresource",
+ gd = "gdscript",
ged = "gedcom",
gmi = "gemtext",
gemini = "gemtext",
gift = "gift",
+ glsl = "glsl",
gpi = "gnuplot",
+ gnuplot = "gnuplot",
go = "go",
gp = "gp",
gs = "grads",
+ gql = "graphql",
+ graphql = "graphql",
+ graphqls = "graphql",
gretl = "gretl",
gradle = "groovy",
groovy = "groovy",
gsp = "gsp",
+ hack = "hack",
+ hackpartial = "hack",
haml = "haml",
hsm = "hamster",
+ hbs = "handlebars",
["hs-boot"] = "haskell",
hsig = "haskell",
hsc = "haskell",
@@ -245,8 +262,11 @@ local extension = {
errsum = "hercules",
ev = "hercules",
vc = "hercules",
+ hcl = "hcl",
+ heex = "heex",
hex = "hex",
["h32"] = "hex",
+ hjson = "hjson",
hog = "hog",
hws = "hollywood",
htt = "httest",
@@ -287,6 +307,7 @@ local extension = {
webmanifest = "json",
ipynb = "json",
["json-patch"] = "json",
+ json5 = "json5",
jsonc = "jsonc",
jsp = "jsp",
jl = "julia",
@@ -303,6 +324,9 @@ local extension = {
lte = "latte",
ld = "ld",
ldif = "ldif",
+ journal = "ledger",
+ ldg = "ledger",
+ ledger = "ledger",
less = "less",
lex = "lex",
lxx = "lex",
@@ -386,6 +410,7 @@ local extension = {
ncf = "ncf",
nginx = "nginx",
ninja = "ninja",
+ nix = "nix",
nqc = "nqc",
roff = "nroff",
tmac = "nroff",
@@ -420,6 +445,7 @@ local extension = {
pcmk = "pcmk",
pdf = "pdf",
plx = "perl",
+ prisma = "prisma",
psgi = "perl",
al = "perl",
ctp = "php",
@@ -463,6 +489,7 @@ local extension = {
["ps1xml"] = "ps1xml",
psf = "psf",
psl = "psl",
+ pug = "pug",
arr = "pyret",
pxd = "pyrex",
pyx = "pyrex",
@@ -470,6 +497,8 @@ local extension = {
py = "python",
pyi = "python",
ptl = "python",
+ ql = "ql",
+ qll = "ql",
rad = "radiance",
mat = "radiance",
["pod6"] = "raku",
@@ -606,6 +635,7 @@ local extension = {
mata = "stata",
ado = "stata",
stp = "stp",
+ sface = "surface",
svelte = "svelte",
svg = "svg",
swift = "swift",
@@ -619,6 +649,7 @@ local extension = {
itcl = "tcl",
tk = "tcl",
jacl = "tcl",
+ tl = "teal",
tmpl = "template",
ti = "terminfo",
dtx = "tex",
@@ -630,7 +661,8 @@ local extension = {
txi = "texinfo",
texinfo = "texinfo",
text = "text",
- tf = "tf",
+ tfvars = "terraform",
+ tla = "tla",
tli = "tli",
toml = "toml",
tpp = "tpp",
@@ -718,6 +750,7 @@ local extension = {
yxx = "yacc",
yml = "yaml",
yaml = "yaml",
+ yang = "yang",
["z8a"] = "z8a",
zig = "zig",
zu = "zimbu",
@@ -732,7 +765,9 @@ local extension = {
PL = function() vim.fn["dist#ft#FTpl"]() end,
R = function() vim.fn["dist#ft#FTr"]() end,
asm = function() vim.fn["dist#ft#FTasm"]() end,
- bas = function() vim.fn["dist#ft#FTVB"]("basic") end,
+ bas = function() vim.fn["dist#ft#FTbas"]() end,
+ bi = function() vim.fn["dist#ft#FTbas"]() end,
+ bm = function() vim.fn["dist#ft#FTbas"]() end,
bash = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
btm = function() vim.fn["dist#ft#FTbtm"]() end,
c = function() vim.fn["dist#ft#FTlpc"]() end,
@@ -770,6 +805,14 @@ local extension = {
mm = function() vim.fn["dist#ft#FTmm"]() end,
mms = function() vim.fn["dist#ft#FTmms"]() end,
p = function() vim.fn["dist#ft#FTprogress_pascal"]() end,
+ patch = function(path, bufnr)
+ local firstline = getline(bufnr, 1)
+ if string.find(firstline, "^From " .. string.rep("%x", 40) .. "+ Mon Sep 17 00:00:00 2001$") then
+ return "gitsendemail"
+ else
+ return "diff"
+ end
+ end,
pl = function() vim.fn["dist#ft#FTpl"]() end,
pp = function() vim.fn["dist#ft#FTpp"]() end,
pro = function() vim.fn["dist#ft#ProtoCheck"]('idlang') end,
@@ -783,6 +826,7 @@ local extension = {
stm = function() vim.fn["dist#ft#FThtml"]() end,
tcsh = function() vim.fn["dist#ft#SetFileTypeShell"]("tcsh") end,
tex = function() vim.fn["dist#ft#FTtex"]() end,
+ tf = function() vim.fn["dist#ft#FTtf"]() end,
w = function() vim.fn["dist#ft#FTprogress_cweb"]() end,
xml = function() vim.fn["dist#ft#FTxml"]() end,
y = function() vim.fn["dist#ft#FTy"]() end,
@@ -860,6 +904,10 @@ local filename = {
["exim.conf"] = "exim",
exports = "exports",
[".fetchmailrc"] = "fetchmail",
+ fvSchemes = function() vim.fn["dist#ft#FTfoam"]() end,
+ fvSolution = function() vim.fn["dist#ft#FTfoam"]() end,
+ fvConstraints = function() vim.fn["dist#ft#FTfoam"]() end,
+ fvModels = function() vim.fn["dist#ft#FTfoam"]() end,
fstab = "fstab",
mtab = "fstab",
[".gdbinit"] = "gdb",
@@ -868,10 +916,10 @@ local filename = {
["TAG_EDITMSG"] = "gitcommit",
["MERGE_MSG"] = "gitcommit",
["COMMIT_EDITMSG"] = "gitcommit",
+ ["NOTES_EDITMSG"] = "gitcommit",
+ ["EDIT_DESCRIPTION"] = "gitcommit",
[".gitconfig"] = "gitconfig",
[".gitmodules"] = "gitconfig",
- ["/.config/git/config"] = "gitconfig",
- ["/etc/gitconfig"] = "gitconfig",
["gitolite.conf"] = "gitolite",
["git-rebase-todo"] = "gitrebase",
gkrellmrc = "gkrellmrc",
@@ -879,6 +927,7 @@ local filename = {
[".gnashpluginrc"] = "gnash",
gnashpluginrc = "gnash",
gnashrc = "gnash",
+ ["go.work"] = "gowork",
[".gprc"] = "gp",
["/.gnupg/gpg.conf"] = "gpg",
["/.gnupg/options"] = "gpg",
@@ -1032,6 +1081,7 @@ local filename = {
["tidy.conf"] = "tidy",
tidyrc = "tidy",
[".tidyrc"] = "tidy",
+ [".tmux.conf"] = "tmux",
["/.cargo/config"] = "toml",
Pipfile = "toml",
["Gopkg.lock"] = "toml",
@@ -1069,6 +1119,7 @@ local filename = {
[".alias"] = function() vim.fn["dist#ft#CSH"]() end,
[".bashrc"] = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
[".cshrc"] = function() vim.fn["dist#ft#CSH"]() end,
+ [".env"] = function() vim.fn["dist#ft#SetFileTypeSH"](vim.fn.getline(1)) end,
[".kshrc"] = function() vim.fn["dist#ft#SetFileTypeSH"]("ksh") end,
[".login"] = function() vim.fn["dist#ft#CSH"]() end,
[".profile"] = function() vim.fn["dist#ft#SetFileTypeSH"](vim.fn.getline(1)) end,
@@ -1124,7 +1175,10 @@ local pattern = {
[".*Eterm/.*%.cfg"] = "eterm",
[".*%.git/modules/.*/config"] = "gitconfig",
[".*%.git/config"] = "gitconfig",
+ [".*/etc/gitconfig"] = "gitconfig",
[".*/%.config/git/config"] = "gitconfig",
+ [".*%.git/config%.worktree"] = "gitconfig",
+ [".*%.git/worktrees/.*/config%.worktree"] = "gitconfig",
["%.gitsendemail%.msg%......."] = "gitsendemail",
["gkrellmrc_."] = "gkrellmrc",
[".*/usr/.*/gnupg/options%.skel"] = "gpg",
@@ -1226,6 +1280,8 @@ local pattern = {
[".*/%.config/systemd/user/.*%.d/.*%.conf"] = "systemd",
[".*/etc/systemd/system/.*%.d/.*%.conf"] = "systemd",
[".*%.t%.html"] = "tilde",
+ ["%.?tmux.*%.conf"] = "tmux",
+ ["%.?tmux.*%.conf.*"] = { "tmux", { priority = -1 } },
[".*/%.cargo/config"] = "toml",
[".*/%.cargo/credentials"] = "toml",
[".*/etc/udev/udev%.conf"] = "udevconf",
@@ -1332,6 +1388,21 @@ local pattern = {
["zlog.*"] = starsetf('zsh'),
["zsh.*"] = starsetf('zsh'),
["ae%d+%.txt"] = 'mail',
+ ["[a-zA-Z0-9].*Dict"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ ["[a-zA-Z0-9].*Dict%..*"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ ["[a-zA-Z].*Properties"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ ["[a-zA-Z].*Properties%..*"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ [".*Transport%..*"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ [".*/constant/g"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ [".*/0/.*"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ [".*/0%.orig/.*"] = function() vim.fn["dist#ft#FTfoam"]() end,
+ [".*/etc/sensors%.d/[^.].*"] = starsetf('sensors'),
+ [".*%.git/.*"] = function(path, bufnr)
+ local firstline = getline(bufnr, 1)
+ if firstline:find("^" .. string.rep("%x", 40) .. "+ ") or firstline:sub(1, 5) == "ref: " then
+ return "git"
+ end
+ end,
-- END PATTERN
}
-- luacheck: pop
@@ -1359,7 +1430,7 @@ local pattern_sorted = sort_by_priority(pattern)
---@private
local function normalize_path(path)
- return (path:gsub("\\", "/"))
+ return (path:gsub("\\", "/"):gsub("^~", vim.env.HOME))
end
--- Add new filetype mappings.
@@ -1454,6 +1525,25 @@ local function dispatch(ft, path, bufnr, ...)
end
---@private
+local function match_pattern(name, path, tail, pat)
+ -- If the pattern contains a / match against the full path, otherwise just the tail
+ local fullpat = "^" .. pat .. "$"
+ local matches
+ if pat:find("/") then
+ -- Similar to |autocmd-pattern|, if the pattern contains a '/' then check for a match against
+ -- both the short file name (as typed) and the full file name (after expanding to full path
+ -- and resolving symlinks)
+ matches = name:match(fullpat) or path:match(fullpat)
+ else
+ matches = tail:match(fullpat)
+ end
+ return matches
+end
+
+--- Set the filetype for the given buffer from a file name.
+---
+---@param name string File name (can be an absolute or relative path)
+---@param bufnr number|nil The buffer to set the filetype for. Defaults to the current buffer.
function M.match(name, bufnr)
-- When fired from the main filetypedetect autocommand the {bufnr} argument is omitted, so we use
-- the current buffer. The {bufnr} argument is provided to allow extensibility in case callers
@@ -1474,21 +1564,18 @@ function M.match(name, bufnr)
return
end
- -- Next, check the file path against available patterns
- for _, v in ipairs(pattern_sorted) do
+ -- Next, check the file path against available patterns with non-negative priority
+ local j = 1
+ for i, v in ipairs(pattern_sorted) do
local k = next(v)
- local ft = v[k][1]
- -- If the pattern contains a / match against the full path, otherwise just the tail
- local pat = "^" .. k .. "$"
- local matches
- if k:find("/") then
- -- Similar to |autocmd-pattern|, if the pattern contains a '/' then check for a match against
- -- both the short file name (as typed) and the full file name (after expanding to full path
- -- and resolving symlinks)
- matches = name:match(pat) or path:match(pat)
- else
- matches = tail:match(pat)
+ local opts = v[k][2]
+ if opts.priority < 0 then
+ j = i
+ break
end
+
+ local ft = v[k][1]
+ local matches = match_pattern(name, path, tail, k)
if matches then
if dispatch(ft, path, bufnr, matches) then
return
@@ -1496,11 +1583,25 @@ function M.match(name, bufnr)
end
end
- -- Finally, check file extension
+ -- Next, check file extension
local ext = vim.fn.fnamemodify(name, ":e")
if dispatch(extension[ext], path, bufnr) then
return
end
+
+ -- Finally, check patterns with negative priority
+ for i = j, #pattern_sorted do
+ local v = pattern_sorted[i]
+ local k = next(v)
+
+ local ft = v[k][1]
+ local matches = match_pattern(name, path, tail, k)
+ if matches then
+ if dispatch(ft, path, bufnr, matches) then
+ return
+ end
+ end
+ end
end
return M
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index 236f3165f2..12faa0a6e1 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -25,16 +25,29 @@ end
---@param higroup highlight group to use for highlighting
---@param rtype type of range (:help setreg, default charwise)
---@param inclusive boolean indicating whether the range is end-inclusive (default false)
-function highlight.range(bufnr, ns, higroup, start, finish, rtype, inclusive)
+---@param priority number indicating priority of highlight (default 50)
+function highlight.range(bufnr, ns, higroup, start, finish, rtype, inclusive, priority)
rtype = rtype or 'v'
inclusive = inclusive or false
+ priority = priority or 50
-- sanity check
if start[2] < 0 or finish[1] < start[1] then return end
local region = vim.region(bufnr, start, finish, rtype, inclusive)
for linenr, cols in pairs(region) do
- api.nvim_buf_add_highlight(bufnr, ns, higroup, linenr, cols[1], cols[2])
+ local end_row
+ if cols[2] == -1 then
+ end_row = linenr + 1
+ cols[2] = 0
+ end
+ api.nvim_buf_set_extmark(bufnr, ns, linenr, cols[1], {
+ hl_group = higroup,
+ end_row = end_row,
+ end_col = cols[2],
+ priority = priority,
+ strict = false
+ })
end
end
@@ -82,7 +95,7 @@ function highlight.on_yank(opts)
pos1 = {pos1[2] - 1, pos1[3] - 1 + pos1[4]}
pos2 = {pos2[2] - 1, pos2[3] - 1 + pos2[4]}
- highlight.range(bufnr, yank_ns, higroup, pos1, pos2, event.regtype, event.inclusive)
+ highlight.range(bufnr, yank_ns, higroup, pos1, pos2, event.regtype, event.inclusive, 200)
vim.defer_fn(
function()
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 7df0064b6b..37e222a5ce 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -306,70 +306,138 @@ local function once(fn)
end
end
-
local changetracking = {}
do
--@private
--- client_id → state
---
--- state
+ --- use_incremental_sync: bool
+ --- buffers: bufnr -> buffer_state
+ ---
+ --- buffer_state
--- pending_change?: function that the timer starts to trigger didChange
--- 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
+ --- Only set if incremental_sync is used
+ ---
--- timer?: uv_timer
+ --- lines: table
local state_by_client = {}
---@private
function changetracking.init(client, bufnr)
+ local use_incremental_sync = (
+ if_nil(client.config.flags.allow_incremental_sync, true)
+ and client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.Incremental
+ )
local state = state_by_client[client.id]
if not state then
state = {
- pending_changes = {};
- use_incremental_sync = (
- if_nil(client.config.flags.allow_incremental_sync, true)
- and client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.Incremental
- );
+ buffers = {};
+ debounce = client.config.flags.debounce_text_changes or 150,
+ use_incremental_sync = use_incremental_sync;
}
state_by_client[client.id] = state
end
- if not state.use_incremental_sync then
- return
- end
- if not state.buffers then
- state.buffers = {}
+ if not state.buffers[bufnr] then
+ local buf_state = {}
+ state.buffers[bufnr] = buf_state
+ if use_incremental_sync then
+ buf_state.lines = nvim_buf_get_lines(bufnr, 0, -1, true)
+ buf_state.lines_tmp = {}
+ buf_state.pending_changes = {}
+ end
end
- state.buffers[bufnr] = nvim_buf_get_lines(bufnr, 0, -1, true)
end
---@private
function changetracking.reset_buf(client, bufnr)
- changetracking.flush(client)
+ changetracking.flush(client, bufnr)
local state = state_by_client[client.id]
if state and state.buffers then
+ local buf_state = state.buffers[bufnr]
state.buffers[bufnr] = nil
+ if buf_state and buf_state.timer then
+ buf_state.timer:stop()
+ buf_state.timer:close()
+ buf_state.timer = nil
+ end
end
end
---@private
function changetracking.reset(client_id)
local state = state_by_client[client_id]
- if state then
- state_by_client[client_id] = nil
- changetracking._reset_timer(state)
+ if not state then
+ return
+ end
+ for _, buf_state in pairs(state.buffers) do
+ if buf_state.timer then
+ buf_state.timer:stop()
+ buf_state.timer:close()
+ buf_state.timer = nil
+ end
end
+ state.buffers = {}
+ end
+
+ ---@private
+ --
+ -- Adjust debounce time by taking time of last didChange notification into
+ -- consideration. If the last didChange happened more than `debounce` time ago,
+ -- debounce can be skipped and otherwise maybe reduced.
+ --
+ -- This turns the debounce into a kind of client rate limiting
+ local function next_debounce(debounce, buf_state)
+ if debounce == 0 then
+ return 0
+ end
+ local ns_to_ms = 0.000001
+ if not buf_state.last_flush then
+ return debounce
+ end
+ local now = uv.hrtime()
+ local ms_since_last_flush = (now - buf_state.last_flush) * ns_to_ms
+ return math.max(debounce - ms_since_last_flush, 0)
end
---@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 incremental_changes = function(client, buf_state)
+
+ local prev_lines = buf_state.lines
+ local curr_lines = buf_state.lines_tmp
+
+ local changed_lines = nvim_buf_get_lines(bufnr, firstline, new_lastline, true)
+ for i = 1, firstline do
+ curr_lines[i] = prev_lines[i]
+ end
+ for i = firstline + 1, new_lastline do
+ curr_lines[i] = changed_lines[i - firstline]
+ end
+ for i = lastline + 1, #prev_lines do
+ curr_lines[i - lastline + new_lastline] = prev_lines[i]
+ end
+ if tbl_isempty(curr_lines) then
+ -- Can happen when deleting the entire contents of a buffer, see https://github.com/neovim/neovim/issues/16259.
+ curr_lines[1] = ''
+ end
+
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)
- cached_buffers[bufnr] = curr_lines
+ buf_state.lines, curr_lines, firstline, lastline, new_lastline, client.offset_encoding or 'utf-16', line_ending)
+
+ -- Double-buffering of lines tables is used to reduce the load on the garbage collector.
+ -- At this point the prev_lines table is useless, but its internal storage has already been allocated,
+ -- so let's keep it around for the next didChange event, in which it will become the next
+ -- curr_lines table. Note that setting elements to nil doesn't actually deallocate slots in the
+ -- internal storage - it merely marks them as free, for the GC to deallocate them.
+ for i in ipairs(prev_lines) do
+ prev_lines[i] = nil
+ end
+ buf_state.lines = curr_lines
+ buf_state.lines_tmp = prev_lines
+
return incremental_change
end
local full_changes = once(function()
@@ -383,75 +451,68 @@ do
return
end
local state = state_by_client[client.id]
- local debounce = client.config.flags.debounce_text_changes or 150
- if debounce == 0 then
- local changes = state.use_incremental_sync and incremental_changes(client) or full_changes()
- client.notify("textDocument/didChange", {
- textDocument = {
- uri = uri;
- version = util.buf_versions[bufnr];
- };
- contentChanges = { changes, }
- })
- return
- end
- changetracking._reset_timer(state)
+ local buf_state = state.buffers[bufnr]
+ changetracking._reset_timer(buf_state)
+ local debounce = next_debounce(state.debounce, buf_state)
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
- if not state.pending_changes[uri] then
- state.pending_changes[uri] = {}
- end
- table.insert(state.pending_changes[uri], incremental_changes(client))
+ table.insert(buf_state.pending_changes, incremental_changes(client, buf_state))
end
- state.pending_change = function()
- state.pending_change = nil
+ buf_state.pending_change = function()
+ buf_state.pending_change = nil
+ buf_state.last_flush = uv.hrtime()
if client.is_stopped() or not vim.api.nvim_buf_is_valid(bufnr) then
return
end
- if state.use_incremental_sync then
- 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
- client.notify("textDocument/didChange", {
- textDocument = {
- uri = uri;
- version = util.buf_versions[bufnr];
- };
- contentChanges = { full_changes() },
- })
- end
+ local changes = state.use_incremental_sync and buf_state.pending_changes or { full_changes() }
+ client.notify("textDocument/didChange", {
+ textDocument = {
+ uri = uri,
+ version = util.buf_versions[bufnr],
+ },
+ contentChanges = changes,
+ })
+ buf_state.pending_changes = {}
+ end
+ if debounce == 0 then
+ buf_state.pending_change()
+ else
+ local timer = vim.loop.new_timer()
+ buf_state.timer = timer
+ -- Must use schedule_wrap because `full_changes()` calls nvim_buf_get_lines
+ timer:start(debounce, 0, vim.schedule_wrap(buf_state.pending_change))
end
- state.timer = vim.loop.new_timer()
- -- Must use schedule_wrap because `full_changes()` calls nvim_buf_get_lines
- state.timer:start(debounce, 0, vim.schedule_wrap(state.pending_change))
end
end
- function changetracking._reset_timer(state)
- if state.timer then
- state.timer:stop()
- state.timer:close()
- state.timer = nil
+ function changetracking._reset_timer(buf_state)
+ if buf_state.timer then
+ buf_state.timer:stop()
+ buf_state.timer:close()
+ buf_state.timer = nil
end
end
--- Flushes any outstanding change notification.
---@private
- function changetracking.flush(client)
+ function changetracking.flush(client, bufnr)
local state = state_by_client[client.id]
- if state then
- changetracking._reset_timer(state)
- if state.pending_change then
- state.pending_change()
+ if not state then
+ return
+ end
+ if bufnr then
+ local buf_state = state.buffers[bufnr] or {}
+ changetracking._reset_timer(buf_state)
+ if buf_state.pending_change then
+ buf_state.pending_change()
+ end
+ else
+ for _, buf_state in pairs(state.buffers) do
+ changetracking._reset_timer(buf_state)
+ if buf_state.pending_change then
+ buf_state.pending_change()
+ end
end
end
end
@@ -957,7 +1018,7 @@ function lsp.start_client(config)
or error(string.format("not found: %q request handler for client %q.", method, client.name))
end
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
- changetracking.flush(client)
+ changetracking.flush(client, bufnr)
bufnr = resolve_bufnr(bufnr)
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr)
local success, request_id = rpc.request(method, params, function(err, result)
@@ -1014,14 +1075,16 @@ function lsp.start_client(config)
---@private
--- Sends a notification to an LSP server.
---
- ---@param method (string) LSP method name.
- ---@param params (optional, table) LSP request params.
- ---@param bufnr (number) Buffer handle, or 0 for current.
+ ---@param method string LSP method name.
+ ---@param params table|nil LSP request params.
---@returns {status} (bool) true if the notification was successful.
---If it is false, then it will always be false
---(the client has shutdown).
- function client.notify(...)
- return rpc.notify(...)
+ function client.notify(method, params)
+ if method ~= 'textDocument/didChange' then
+ changetracking.flush(client)
+ end
+ return rpc.notify(method, params)
end
---@private
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 543fdb0237..eb7ec579f1 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -184,7 +184,7 @@ function M.formatting_sync(options, timeout_ms)
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)
+ util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then
vim.notify('vim.lsp.buf.formatting_sync: ' .. err, vim.log.levels.WARN)
end
@@ -228,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, bufnr)
+ util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then
vim.notify(string.format("vim.lsp.buf.formatting_seq_sync: (%s) %s", client.name, err), vim.log.levels.WARN)
end
@@ -447,6 +447,9 @@ end
---@param query (string, optional)
function M.workspace_symbol(query)
query = query or npcall(vfn.input, "Query: ")
+ if query == nil then
+ return
+ end
local params = {query = query}
request('workspace/symbol', params)
end
@@ -503,7 +506,7 @@ local function on_code_action_results(results, ctx)
---@private
local function apply_action(action, client)
if action.edit then
- util.apply_workspace_edit(action.edit)
+ util.apply_workspace_edit(action.edit, client.offset_encoding)
end
if action.command then
local command = type(action.command) == 'table' and action.command or action
@@ -627,14 +630,19 @@ end
--- Executes an LSP server command.
---
----@param command A valid `ExecuteCommandParams` object
+---@param command_params table A valid `ExecuteCommandParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
-function M.execute_command(command)
+function M.execute_command(command_params)
validate {
- command = { command.command, 's' },
- arguments = { command.arguments, 't', true }
+ command = { command_params.command, 's' },
+ arguments = { command_params.arguments, 't', true }
+ }
+ command_params = {
+ command=command_params.command,
+ arguments=command_params.arguments,
+ workDoneToken=command_params.workDoneToken,
}
- request('workspace/executeCommand', command)
+ request('workspace/executeCommand', command_params )
end
return M
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index f38b469f3c..68942ae11a 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -168,8 +168,8 @@ end
--- },
--- -- Use a function to dynamically turn signs off
--- -- and on, using buffer local variables
---- signs = function(bufnr, client_id)
---- return vim.bo[bufnr].show_signs == false
+--- signs = function(namespace, bufnr)
+--- return vim.b[bufnr].show_signs == true
--- end,
--- -- Disable a feature
--- update_in_insert = false,
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index a48302cc4b..a997b887d9 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -111,13 +111,15 @@ M['client/registerCapability'] = function(_, _, ctx)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
-M['workspace/applyEdit'] = function(_, workspace_edit)
+M['workspace/applyEdit'] = function(_, workspace_edit, ctx)
if not workspace_edit then return end
-- TODO(ashkan) Do something more with label?
+ local client_id = ctx.client_id
+ local client = vim.lsp.get_client_by_id(client_id)
if workspace_edit.label then
print("Workspace edit", workspace_edit.label)
end
- local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit)
+ local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding)
return {
applied = status;
failureReason = result;
@@ -150,6 +152,17 @@ M['workspace/configuration'] = function(_, result, ctx)
return response
end
+--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders
+M['workspace/workspaceFolders'] = function(_, _, ctx)
+ local client_id = ctx.client_id
+ local client = vim.lsp.get_client_by_id(client_id)
+ if not client then
+ err_message("LSP[id=", client_id, "] client has shut down after sending the message")
+ return
+ end
+ return client.workspace_folders or vim.NIL
+end
+
M['textDocument/publishDiagnostics'] = function(...)
return require('vim.lsp.diagnostic').on_publish_diagnostics(...)
end
@@ -159,6 +172,31 @@ M['textDocument/codeLens'] = function(...)
end
+--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
+M['textDocument/references'] = function(_, result, ctx, config)
+ if not result or vim.tbl_isempty(result) then
+ vim.notify('No references found')
+ else
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
+ config = config or {}
+ if config.loclist then
+ vim.fn.setloclist(0, {}, ' ', {
+ title = 'References';
+ items = util.locations_to_items(result, client.offset_encoding);
+ context = ctx;
+ })
+ api.nvim_command("lopen")
+ else
+ vim.fn.setqflist({}, ' ', {
+ title = 'References';
+ items = util.locations_to_items(result, client.offset_encoding);
+ context = ctx;
+ })
+ api.nvim_command("botright copen")
+ end
+ end
+end
+
---@private
--- Return a function that converts LSP responses to list items and opens the list
@@ -169,23 +207,26 @@ end
--- loclist: (boolean) use the location list (default is to use the quickfix list)
---
---@param map_result function `((resp, bufnr) -> list)` to convert the response
----@param entity name of the resource used in a `not found` error message
-local function response_to_list(map_result, entity)
- return function(_,result, ctx, config)
+---@param entity string name of the resource used in a `not found` error message
+---@param title_fn function Function to call to generate list title
+local function response_to_list(map_result, entity, title_fn)
+ return function(_, result, ctx, config)
if not result or vim.tbl_isempty(result) then
vim.notify('No ' .. entity .. ' found')
else
config = config or {}
if config.loclist then
vim.fn.setloclist(0, {}, ' ', {
- title = 'Language Server';
+ title = title_fn(ctx);
items = map_result(result, ctx.bufnr);
+ context = ctx;
})
api.nvim_command("lopen")
else
vim.fn.setqflist({}, ' ', {
- title = 'Language Server';
+ title = title_fn(ctx);
items = map_result(result, ctx.bufnr);
+ context = ctx;
})
api.nvim_command("botright copen")
end
@@ -194,31 +235,36 @@ local function response_to_list(map_result, entity)
end
---see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
-M['textDocument/references'] = response_to_list(util.locations_to_items, 'references')
-
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
-M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols')
+M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx)
+ local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ":.")
+ return string.format('Symbols in %s', fname)
+end)
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol
-M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols')
+M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols', function(ctx)
+ return string.format("Symbols matching '%s'", ctx.params.query)
+end)
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename
-M['textDocument/rename'] = function(_, result, _)
+M['textDocument/rename'] = function(_, result, ctx, _)
if not result then return end
- util.apply_workspace_edit(result)
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
+ util.apply_workspace_edit(result, client.offset_encoding)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
M['textDocument/rangeFormatting'] = function(_, result, ctx, _)
if not result then return end
- util.apply_text_edits(result, ctx.bufnr)
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
+ util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
M['textDocument/formatting'] = function(_, result, ctx, _)
if not result then return end
- util.apply_text_edits(result, ctx.bufnr)
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
+ util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
@@ -277,19 +323,23 @@ local function location_handler(_, result, ctx, _)
local _ = log.info() and log.info(ctx.method, 'No location found')
return nil
end
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
-- textDocument/definition can return Location or Location[]
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
if vim.tbl_islist(result) then
- util.jump_to_location(result[1])
+ util.jump_to_location(result[1], client.offset_encoding)
if #result > 1 then
- vim.fn.setqflist({}, ' ', {title = 'LSP locations', items = util.locations_to_items(result)})
+ vim.fn.setqflist({}, ' ', {
+ title = 'LSP locations',
+ items = util.locations_to_items(result, client.offset_encoding)
+ })
api.nvim_command("copen")
end
else
- util.jump_to_location(result)
+ util.jump_to_location(result, client.offset_encoding)
end
end
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 0af4fcb036..d22c00ae76 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -193,6 +193,11 @@ end
local function get_lines(bufnr, rows)
rows = type(rows) == "table" and rows or { rows }
+ -- This is needed for bufload and bufloaded
+ if bufnr == 0 then
+ bufnr = vim.api.nvim_get_current_buf()
+ end
+
---@private
local function buf_lines()
local lines = {}
@@ -272,7 +277,7 @@ 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
+---@param offset_encoding string utf-8|utf-16|utf-32
--- 1-indexed
local function get_line_byte_from_position(bufnr, position, offset_encoding)
-- LSP's line and characters are 0-indexed
@@ -281,7 +286,7 @@ 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
- local line = get_line(bufnr, position.line)
+ local line = get_line(bufnr, position.line) or ''
local ok, result
ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding)
if ok then
@@ -355,15 +360,14 @@ 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`
+---@param offset_encoding string utf-8|utf-16|utf-32 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, offset_encoding)
validate {
text_edits = { text_edits, 't', false };
bufnr = { bufnr, 'number', false };
- offset_encoding = { offset_encoding, 'string', true };
+ offset_encoding = { offset_encoding, 'string', false };
}
- 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)
@@ -397,25 +401,6 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
end
end)
- -- 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)
- 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
- text_edit.range.start.character = len
- text_edit.newText = '\n' .. text_edit.newText
- has_eol_text_edit = true
- end
- if max <= text_edit.range['end'].line then
- text_edit.range['end'].line = max - 1
- text_edit.range['end'].character = len
- has_eol_text_edit = true
- end
- return text_edit
- end, text_edits)
-
-- Some LSP servers are depending on the VSCode behavior.
-- The VSCode will re-locate the cursor position after applying TextEdit so we also do it.
local is_current_buf = vim.api.nvim_get_current_buf() == bufnr
@@ -435,16 +420,38 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
-- Apply text edits.
local is_cursor_fixed = false
+ local has_eol_text_edit = false
for _, text_edit in ipairs(text_edits) do
+ -- Normalize line ending
+ text_edit.newText, _ = string.gsub(text_edit.newText, '\r\n?', '\n')
+
+ -- Convert from LSP style ranges to Neovim style ranges.
local e = {
start_row = text_edit.range.start.line,
- start_col = get_line_byte_from_position(bufnr, text_edit.range.start),
+ start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding),
end_row = text_edit.range['end'].line,
- end_col = get_line_byte_from_position(bufnr, text_edit.range['end']),
+ end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding),
text = vim.split(text_edit.newText, '\n', true),
}
+
+ -- 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 max = vim.api.nvim_buf_line_count(bufnr)
+ if max <= e.start_row or max <= e.end_row then
+ local len = #(get_line(bufnr, max - 1) or '')
+ if max <= e.start_row then
+ e.start_row = max - 1
+ e.start_col = len
+ table.insert(e.text, 1, '')
+ end
+ if max <= e.end_row then
+ e.end_row = max - 1
+ e.end_col = len
+ end
+ has_eol_text_edit = true
+ end
vim.api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text)
+ -- Fix cursor position.
local row_count = (e.end_row - e.start_row) + 1
if e.end_row < cursor.row then
cursor.row = cursor.row + (#e.text - row_count)
@@ -459,10 +466,13 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
end
end
+ local max = vim.api.nvim_buf_line_count(bufnr)
+
+ -- Apply fixed cursor position.
if is_cursor_fixed then
local is_valid_cursor = true
- is_valid_cursor = is_valid_cursor and cursor.row < vim.api.nvim_buf_line_count(bufnr)
- is_valid_cursor = is_valid_cursor and cursor.col <= #(vim.api.nvim_buf_get_lines(bufnr, cursor.row, cursor.row + 1, false)[1] or '')
+ is_valid_cursor = is_valid_cursor and cursor.row < max
+ is_valid_cursor = is_valid_cursor and cursor.col <= #(get_line(bufnr, max - 1) or '')
if is_valid_cursor then
vim.api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col })
end
@@ -471,7 +481,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
-- Remove final line if needed
local fix_eol = has_eol_text_edit
fix_eol = fix_eol and api.nvim_buf_get_option(bufnr, 'fixeol')
- fix_eol = fix_eol and (vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '') == ''
+ fix_eol = fix_eol and get_line(bufnr, max - 1) == ''
if fix_eol then
vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {})
end
@@ -509,9 +519,12 @@ end
---@param text_document_edit table: a `TextDocumentEdit` object
---@param index number: Optional index of the edit, if from a list of edits (or nil, if not from a list)
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
-function M.apply_text_document_edit(text_document_edit, index)
+function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
local text_document = text_document_edit.textDocument
local bufnr = vim.uri_to_bufnr(text_document.uri)
+ if offset_encoding == nil then
+ vim.notify_once("apply_text_document_edit must be called with valid offset encoding", vim.log.levels.WARN)
+ end
-- For lists of text document edits,
-- do not check the version after the first edit.
@@ -530,7 +543,7 @@ function M.apply_text_document_edit(text_document_edit, index)
return
end
- M.apply_text_edits(text_document_edit.edits, bufnr)
+ M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding)
end
--- Parses snippets in a completion entry.
@@ -727,9 +740,13 @@ end
--- Applies a `WorkspaceEdit`.
---
----@param workspace_edit (table) `WorkspaceEdit`
+---@param workspace_edit table `WorkspaceEdit`
+---@param offset_encoding string utf-8|utf-16|utf-32 (required)
--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
-function M.apply_workspace_edit(workspace_edit)
+function M.apply_workspace_edit(workspace_edit, offset_encoding)
+ if offset_encoding == nil then
+ vim.notify_once("apply_workspace_edit must be called with valid offset encoding", vim.log.levels.WARN)
+ end
if workspace_edit.documentChanges then
for idx, change in ipairs(workspace_edit.documentChanges) do
if change.kind == "rename" then
@@ -745,7 +762,7 @@ function M.apply_workspace_edit(workspace_edit)
elseif change.kind then
error(string.format("Unsupported change: %q", vim.inspect(change)))
else
- M.apply_text_document_edit(change, idx)
+ M.apply_text_document_edit(change, idx, offset_encoding)
end
end
return
@@ -758,7 +775,7 @@ function M.apply_workspace_edit(workspace_edit)
for uri, changes in pairs(all_changes) do
local bufnr = vim.uri_to_bufnr(uri)
- M.apply_text_edits(changes, bufnr)
+ M.apply_text_edits(changes, bufnr, offset_encoding)
end
end
@@ -834,7 +851,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
local active_hl
local active_signature = signature_help.activeSignature or 0
-- If the activeSignature is not inside the valid range, then clip it.
- if active_signature >= #signature_help.signatures then
+ -- In 3.15 of the protocol, activeSignature was allowed to be negative
+ if active_signature >= #signature_help.signatures or active_signature < 0 then
active_signature = 0
end
local signature = signature_help.signatures[active_signature + 1]
@@ -975,12 +993,16 @@ end
--- Jumps to a location.
---
----@param location (`Location`|`LocationLink`)
+---@param location table (`Location`|`LocationLink`)
+---@param offset_encoding string utf-8|utf-16|utf-32 (required)
---@returns `true` if the jump succeeded
-function M.jump_to_location(location)
+function M.jump_to_location(location, offset_encoding)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
if uri == nil then return end
+ if offset_encoding == nil then
+ vim.notify_once("jump_to_location must be called with valid offset encoding", vim.log.levels.WARN)
+ end
local bufnr = vim.uri_to_bufnr(uri)
-- Save position in jumplist
vim.cmd "normal! m'"
@@ -995,7 +1017,7 @@ function M.jump_to_location(location)
api.nvim_buf_set_option(bufnr, 'buflisted', true)
local range = location.range or location.targetSelectionRange
local row = range.start.line
- local col = get_line_byte_from_position(bufnr, range.start)
+ local col = get_line_byte_from_position(bufnr, range.start, offset_encoding)
api.nvim_win_set_cursor(0, {row + 1, col})
-- Open folds under the cursor
vim.cmd("normal! zv")
@@ -1504,11 +1526,13 @@ do --[[ References ]]
---
---@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`
+ ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32".
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
function M.buf_highlight_references(bufnr, references, offset_encoding)
- validate { bufnr = {bufnr, 'n', true} }
- offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
+ validate {
+ bufnr = {bufnr, 'n', true},
+ offset_encoding = { offset_encoding, 'string', false };
+ }
for _, reference in ipairs(references) do
local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"]
local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"]
@@ -1526,7 +1550,10 @@ do --[[ References ]]
reference_ns,
document_highlight_kind[kind],
{ start_line, start_idx },
- { end_line, end_idx })
+ { end_line, end_idx },
+ nil,
+ false,
+ 40)
end
end
end
@@ -1541,9 +1568,14 @@ end)
--- The result can be passed to the {list} argument of |setqflist()| or
--- |setloclist()|.
---
----@param locations (table) list of `Location`s or `LocationLink`s
+---@param locations table list of `Location`s or `LocationLink`s
+---@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32
---@returns (table) list of items
-function M.locations_to_items(locations)
+function M.locations_to_items(locations, offset_encoding)
+ if offset_encoding == nil then
+ vim.notify_once("locations_to_items must be called with valid offset encoding", vim.log.levels.WARN)
+ end
+
local items = {}
local grouped = setmetatable({}, {
__index = function(t, k)
@@ -1583,7 +1615,7 @@ function M.locations_to_items(locations)
local pos = temp.start
local row = pos.line
local line = lines[row] or ""
- local col = pos.character
+ local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
table.insert(items, {
filename = filename,
lnum = row + 1,
@@ -1767,7 +1799,13 @@ function M._get_offset_encoding(bufnr)
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 client.offset_encoding == nil then
+ vim.notify_once(
+ string.format("Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.", client.id),
+ vim.log.levels.ERROR
+ )
+ end
+ local this_offset_encoding = client.offset_encoding
if not offset_encoding then
offset_encoding = this_offset_encoding
elseif offset_encoding ~= this_offset_encoding then
@@ -1814,7 +1852,7 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
end_pos = {end_pos, 't', true};
offset_encoding = {offset_encoding, 's', true};
}
- bufnr = bufnr or 0
+ bufnr = bufnr or vim.api.nvim_get_current_buf()
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, '>'))
@@ -1896,7 +1934,9 @@ end
---@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 offset_encoding == nil then
+ vim.notify_once("character_offset must be called with valid offset encoding", vim.log.levels.WARN)
+ end
-- If the col is past the EOL, use the line length.
if col > #line then
return _str_utfindex_enc(line, nil, offset_encoding)
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 07f6418c0c..f9d539f028 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -11,6 +11,7 @@ local parsers = {}
local M = vim.tbl_extend("error", query, language)
M.language_version = vim._ts_get_language_version()
+M.minimum_language_version = vim._ts_get_minimum_language_version()
setmetatable(M, {
__index = function (t, k)
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index 6f347ff25f..8b106108df 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -14,7 +14,7 @@ function M.require_language(lang, path, silent)
return true
end
if path == nil then
- local fname = 'parser/' .. lang .. '.*'
+ local fname = 'parser/' .. vim.fn.fnameescape(lang) .. '.*'
local paths = a.nvim_get_runtime_file(fname, false)
if #paths == 0 then
if silent then
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index ebed502c92..b3036ea679 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -138,6 +138,13 @@ function M.get_query(lang, query_name)
end
end
+local query_cache = setmetatable({}, {
+ __index = function(tbl, key)
+ rawset(tbl, key, {})
+ return rawget(tbl, key)
+ end
+})
+
--- Parse {query} as a string. (If the query is in a file, the caller
--- should read the contents into a string before calling).
---
@@ -151,17 +158,23 @@ end
--- -` info.captures` also points to `captures`.
--- - `info.patterns` contains information about predicates.
---
----@param lang The language
----@param query A string containing the query (s-expr syntax)
+---@param lang string The language
+---@param query string A string containing the query (s-expr syntax)
---
---@returns The query
function M.parse_query(lang, query)
language.require_language(lang)
- local self = setmetatable({}, Query)
- self.query = vim._ts_parse_query(lang, query)
- self.info = self.query:inspect()
- self.captures = self.info.captures
- return self
+ local cached = query_cache[lang][query]
+ if cached then
+ return cached
+ else
+ local self = setmetatable({}, Query)
+ self.query = vim._ts_parse_query(lang, query)
+ self.info = self.query:inspect()
+ self.captures = self.info.captures
+ query_cache[lang][query] = self
+ return self
+ end
end
--- Gets the text corresponding to a given node
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index f9978a6b00..49d9389773 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 Dec 16
+" Last Change: 2022 Jan 17
"
" WORK IN PROGRESS - Only the basics work
" Note: On MS-Windows you need a recent version of gdb. The one included with
@@ -153,7 +153,7 @@ func s:StartDebug_internal(dict)
if &columns < g:termdebug_wide
let s:save_columns = &columns
let &columns = g:termdebug_wide
- " If we make the Vim window wider, use the whole left halve for the debug
+ " If we make the Vim window wider, use the whole left half for the debug
" windows.
let s:allleft = 1
endif
@@ -1051,10 +1051,10 @@ func s:GetEvaluationExpression(range, arg)
return expr
endfunc
-" clean up expression that may got in because of range
+" clean up expression that may get 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
+" 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')
@@ -1084,7 +1084,7 @@ func s:HandleEvaluate(msg)
\ ->substitute('.*value="\(.*\)"', '\1', '')
\ ->substitute('\\"', '"', 'g')
\ ->substitute('\\\\', '\\', 'g')
- "\ multi-byte characters arrive in octal form, replace everthing but NULL values
+ "\ multi-byte characters arrive in octal form, replace everything 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
@@ -1344,6 +1344,16 @@ func s:HandleCursor(msg)
if lnum =~ '^[0-9]*$'
call s:GotoSourcewinOrCreateIt()
if expand('%:p') != fnamemodify(fname, ':p')
+ echomsg 'different fname: "' .. expand('%:p') .. '" vs "' .. fnamemodify(fname, ':p') .. '"'
+ augroup Termdebug
+ " Always open a file read-only instead of showing the ATTENTION
+ " prompt, since we are unlikely to want to edit the file.
+ " The file may be changed but not saved, warn for that.
+ au SwapExists * echohl WarningMsg
+ \ | echo 'Warning: file is being edited elsewhere'
+ \ | echohl None
+ \ | let v:swapchoice = 'o'
+ augroup END
if &modified
" TODO: find existing window
exe 'split ' . fnameescape(fname)
@@ -1352,6 +1362,9 @@ func s:HandleCursor(msg)
else
exe 'edit ' . fnameescape(fname)
endif
+ augroup Termdebug
+ au! SwapExists
+ augroup END
endif
exe lnum
normal! zv
diff --git a/runtime/scripts.vim b/runtime/scripts.vim
index 3790b1c10f..dd47f65ba0 100644
--- a/runtime/scripts.vim
+++ b/runtime/scripts.vim
@@ -384,7 +384,7 @@ else
set ft=scheme
" Git output
- elseif s:line1 =~# '^\(commit\|tree\|object\) \x\{40\}\>\|^tag \S\+$'
+ elseif s:line1 =~# '^\(commit\|tree\|object\) \x\{40,\}\>\|^tag \S\+$'
set ft=git
" Gprof (gnu profiler)
@@ -406,6 +406,12 @@ else
elseif s:line1 =~# '^#.*by RouterOS.*$'
set ft=routeros
+ " Sed scripts
+ " #ncomment is allowed but most likely a false positive so require a space
+ " before any trailing comment text
+ elseif s:line1 =~# '^#n\%($\|\s\)'
+ set ft=sed
+
" CVS diff
else
let s:lnum = 1
diff --git a/runtime/syntax/git.vim b/runtime/syntax/git.vim
index a8467edd43..bf013ce195 100644
--- a/runtime/syntax/git.vim
+++ b/runtime/syntax/git.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: generic git output
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2019 Dec 05
+" Last Change: 2022 Jan 05
if exists("b:current_syntax")
finish
@@ -12,12 +12,28 @@ syn sync minlines=50
syn include @gitDiff syntax/diff.vim
-syn region gitHead start=/\%^/ end=/^$/
-syn region gitHead start=/\%(^commit\%( \x\{40\}\)\{1,\}\%(\s*(.*)\)\=$\)\@=/ end=/^$/
-
-" For git reflog and git show ...^{tree}, avoid sync issues
-syn match gitHead /^\d\{6\} \%(\w\{4} \)\=\x\{40\}\%( [0-3]\)\=\t.*/
-syn match gitHead /^\x\{40\} \x\{40}\t.*/
+syn region gitHead start=/\%^\%(tag \|tree \|object \)\@=/ end=/^$/ contains=@NoSpell
+syn region gitHead start=/\%(^commit\%( \x\{4,\}\)\{1,\}\%(\s*(.*)\)\=$\)\@=/ end=/^$/ contains=@NoSpell
+" git log --oneline
+" minimize false positives by verifying contents of buffer
+if getline(1) =~# '^\x\{7,\} ' && getline('$') =~# '^\x\{7,\} '
+ syn match gitHashAbbrev /^\x\{7,\} \@=/ contains=@NoSpell
+elseif getline(1) =~# '^[|\/\\_ ]\{-\}\*[|\/\\_ ]\{-\} \x\{7,\} '
+ syn match gitHashAbbrev /^[|\/\\_ ]\{-\}\*[|\/\\_ ]\{-\} \zs\x\{7,\} \@=/ contains=@NoSpell
+endif
+" git log --graph
+syn region gitGraph start=/\%(^[|\/\\_ ]*\*[|\/\\_ ]\{-\} commit\%( \x\{4,\}\)\{1,\}\%(\s*(.*)\)\=$\)\@=/ end=/^\%([|\/\\_ ]*$\)\@=/ contains=@NoSpell
+" git blame --porcelain
+syn region gitHead start=/\%(^\x\{40,\} \d\+ \d\+\%( \d\+\)\=$\)\@=/ end=/^\t\@=/ contains=@NoSpell
+" git ls-tree
+syn match gitMode /^\d\{6\}\%( \%(blob\|tree\) \x\{4,\}\t\)\@=/ nextgroup=gitType skipwhite contains=@NoSpell
+" git ls-files --stage
+syn match gitMode /^\d\{6\}\%( \x\{4,\} [0-3]\t\)\@=/ nextgroup=gitHashStage skipwhite contains=@NoSpell
+" .git/HEAD, .git/refs/
+syn match gitKeyword /\%^ref: \@=/ nextgroup=gitReference skipwhite contains=@NoSpell
+syn match gitHash /\%^\x\{40,}\%$/ skipwhite contains=@NoSpell
+" .git/logs/
+syn match gitReflog /^\x\{40,\} \x\{40,\} .\{-\}\d\+\s-\d\{4\}\t.*/ skipwhite contains=@NoSpell,gitReflogOld
syn region gitDiff start=/^\%(diff --git \)\@=/ end=/^\%(diff --\|$\)\@=/ contains=@gitDiff fold
syn region gitDiff start=/^\%(@@ -\)\@=/ end=/^\%(diff --\%(git\|cc\|combined\) \|$\)\@=/ contains=@gitDiff
@@ -25,35 +41,47 @@ syn region gitDiff start=/^\%(@@ -\)\@=/ end=/^\%(diff --\%(git\|cc\|combined\)
syn region gitDiffMerge start=/^\%(diff --\%(cc\|combined\) \)\@=/ end=/^\%(diff --\|$\)\@=/ contains=@gitDiff
syn region gitDiffMerge start=/^\%(@@@@* -\)\@=/ end=/^\%(diff --\|$\)\@=/ contains=@gitDiff
syn match gitDiffAdded "^ \++.*" contained containedin=gitDiffMerge
-syn match gitDiffAdded "{+.*+}" contained containedin=gitDiff
+syn match gitDiffAdded "{+[^}]*+}" contained containedin=gitDiff
syn match gitDiffRemoved "^ \+-.*" contained containedin=gitDiffMerge
-syn match gitDiffRemoved "\[-.*-\]" contained containedin=gitDiff
+syn match gitDiffRemoved "\[-[^]]*-\]" contained containedin=gitDiff
+
+syn match gitKeyword /^commit \@=/ contained containedin=gitHead nextgroup=gitHashAbbrev skipwhite contains=@NoSpell
+syn match gitKeyword /^\%(object\|tree\|parent\|encoding\|gpgsig\%(-\w\+\)\=\|previous\) \@=/ contained containedin=gitHead nextgroup=gitHash skipwhite contains=@NoSpell
+syn match gitKeyword /^Merge:/ contained containedin=gitHead nextgroup=gitHashAbbrev skipwhite contains=@NoSpell
+syn match gitIdentityKeyword /^\%(author\|committer\|tagger\) \@=/ contained containedin=gitHead nextgroup=gitIdentity skipwhite contains=@NoSpell
+syn match gitIdentityHeader /^\%(Author\|Commit\|Tagger\):/ contained containedin=gitHead nextgroup=gitIdentity skipwhite contains=@NoSpell
+syn match gitDateHeader /^\%(AuthorDate\|CommitDate\|Date\):/ contained containedin=gitHead nextgroup=gitDate skipwhite contains=@NoSpell
-syn match gitKeyword /^\%(object\|type\|tag\|commit\|tree\|parent\|encoding\)\>/ contained containedin=gitHead nextgroup=gitHash,gitType skipwhite
-syn match gitKeyword /^\%(tag\>\|ref:\)/ contained containedin=gitHead nextgroup=gitReference skipwhite
-syn match gitKeyword /^Merge:/ contained containedin=gitHead nextgroup=gitHashAbbrev skipwhite
-syn match gitMode /^\d\{6\}\>/ contained containedin=gitHead nextgroup=gitType,gitHash skipwhite
-syn match gitIdentityKeyword /^\%(author\|committer\|tagger\)\>/ contained containedin=gitHead nextgroup=gitIdentity skipwhite
-syn match gitIdentityHeader /^\%(Author\|Commit\|Tagger\):/ contained containedin=gitHead nextgroup=gitIdentity skipwhite
-syn match gitDateHeader /^\%(AuthorDate\|CommitDate\|Date\):/ contained containedin=gitHead nextgroup=gitDate skipwhite
+syn match gitKeyword /^[*|\/\\_ ]\+\zscommit \@=/ contained containedin=gitGraph nextgroup=gitHashAbbrev skipwhite contains=@NoSpell
+syn match gitKeyword /^[|\/\\_ ]\+\zs\%(object\|tree\|parent\|encoding\|gpgsig\%(-\w\+\)\=\|previous\) \@=/ contained containedin=gitGraph nextgroup=gitHash skipwhite contains=@NoSpell
+syn match gitKeyword /^[|\/\\_ ]\+\zsMerge:/ contained containedin=gitGraph nextgroup=gitHashAbbrev skipwhite contains=@NoSpell
+syn match gitIdentityKeyword /^[|\/\\_ ]\+\zs\%(author\|committer\|tagger\) \@=/ contained containedin=gitGraph nextgroup=gitIdentity skipwhite contains=@NoSpell
+syn match gitIdentityHeader /^[|\/\\_ ]\+\zs\%(Author\|Commit\|Tagger\):/ contained containedin=gitGraph nextgroup=gitIdentity skipwhite contains=@NoSpell
+syn match gitDateHeader /^[|\/\\_ ]\+\zs\%(AuthorDate\|CommitDate\|Date\):/ contained containedin=gitGraph nextgroup=gitDate skipwhite contains=@NoSpell
-syn match gitReflogHeader /^Reflog:/ contained containedin=gitHead nextgroup=gitReflogMiddle skipwhite
-syn match gitReflogHeader /^Reflog message:/ contained containedin=gitHead skipwhite
-syn match gitReflogMiddle /\S\+@{\d\+} (/he=e-2 nextgroup=gitIdentity
+syn match gitKeyword /^type \@=/ contained containedin=gitHead nextgroup=gitType skipwhite contains=@NoSpell
+syn match gitKeyword /^\%(summary\|boundary\|filename\|\%(author\|committer\)-\%(time\|tz\)\) \@=/ contained containedin=gitHead skipwhite contains=@NoSpell
+syn match gitKeyword /^tag \@=/ contained containedin=gitHead nextgroup=gitReference skipwhite contains=@NoSpell
+syn match gitIdentityKeyword /^\%(author\|committer\)-mail \@=/ contained containedin=gitHead nextgroup=gitEmail skipwhite contains=@NoSpell
+syn match gitReflogHeader /^Reflog:/ contained containedin=gitHead nextgroup=gitReflogMiddle skipwhite contains=@NoSpell
+syn match gitReflogHeader /^Reflog message:/ contained containedin=gitHead skipwhite contains=@NoSpell
+syn match gitReflogMiddle /\S\+@{\d\+} (/he=e-2 nextgroup=gitIdentity contains=@NoSpell
-syn match gitDate /\<\u\l\l \u\l\l \d\=\d \d\d:\d\d:\d\d \d\d\d\d [+-]\d\d\d\d/ contained
-syn match gitDate /-\=\d\+ [+-]\d\d\d\d\>/ contained
-syn match gitDate /\<\d\+ \l\+ ago\>/ contained
-syn match gitType /\<\%(tag\|commit\|tree\|blob\)\>/ contained nextgroup=gitHash skipwhite
-syn match gitStage /\<\d\t\@=/ contained
-syn match gitReference /\S\+\S\@!/ contained
-syn match gitHash /\<\x\{40\}\>/ contained nextgroup=gitIdentity,gitStage,gitHash skipwhite
-syn match gitHash /^\<\x\{40\}\>/ containedin=gitHead contained nextgroup=gitHash skipwhite
-syn match gitHashAbbrev /\<\x\{4,40\}\>/ contained nextgroup=gitHashAbbrev skipwhite
-syn match gitHashAbbrev /\<\x\{4,39\}\.\.\./he=e-3 contained nextgroup=gitHashAbbrev skipwhite
+syn match gitIdentity /\S.\{-\} <[^>]*>/ contained nextgroup=gitDate skipwhite contains=@NoSpell
+syn region gitEmail matchgroup=gitEmailDelimiter start=/</ end=/>/ keepend oneline contained containedin=gitIdentity contains=@NoSpell
+syn match gitDate /\<\u\l\l \u\l\l \d\=\d \d\d:\d\d:\d\d \d\d\d\d [+-]\d\d\d\d/ contained contains=@NoSpell
+syn match gitDate /-\=\d\+ [+-]\d\d\d\d\>/ contained contains=@NoSpell
+syn match gitDate /\<\d\+ \l\+ ago\>/ contained contains=@NoSpell
+syn match gitType /\<\%(tag\|commit\|tree\|blob\)\>/ contained nextgroup=gitHashAbbrev skipwhite contains=@NoSpell
+syn match gitReference /\S\+\S\@!/ contained contains=@NoSpell
+syn match gitHash /\<\x\{40,\}\>/ contained nextgroup=gitIdentity,gitHash skipwhite contains=@NoSpell
+syn match gitReflogOld /^\x\{40,\} \@=/ contained nextgroup=gitReflogNew skipwhite contains=@NoSpell
+syn match gitReflogNew /\<\x\{40,\} \@=/ contained nextgroup=gitIdentity skipwhite contains=@NoSpell
+syn match gitHashAbbrev /\<\x\{4,\}\>/ contained nextgroup=gitHashAbbrev skipwhite contains=@NoSpell
+syn match gitHashAbbrev /\<\x\{4,39\}\.\.\./he=e-3 contained nextgroup=gitHashAbbrev skipwhite contains=@NoSpell
+syn match gitHashStage /\<\x\{4,\}\>/ contained nextgroup=gitStage skipwhite contains=@NoSpell
+syn match gitStage /\<\d\t\@=/ contained contains=@NoSpell
-syn match gitIdentity /\S.\{-\} <[^>]*>/ contained nextgroup=gitDate skipwhite
-syn region gitEmail matchgroup=gitEmailDelimiter start=/</ end=/>/ keepend oneline contained containedin=gitIdentity
syn match gitNotesHeader /^Notes:\ze\n /
@@ -68,7 +96,10 @@ hi def link gitEmailDelimiter Delimiter
hi def link gitEmail Special
hi def link gitDate Number
hi def link gitMode Number
+hi def link gitHashStage gitHash
hi def link gitHashAbbrev gitHash
+hi def link gitReflogOld gitHash
+hi def link gitReflogNew gitHash
hi def link gitHash Identifier
hi def link gitReflogMiddle gitReference
hi def link gitReference Function
diff --git a/runtime/syntax/gitcommit.vim b/runtime/syntax/gitcommit.vim
index 63b1ce920f..42c8d4414f 100644
--- a/runtime/syntax/gitcommit.vim
+++ b/runtime/syntax/gitcommit.vim
@@ -2,71 +2,87 @@
" Language: git commit file
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" Filenames: *.git/COMMIT_EDITMSG
-" Last Change: 2019 Dec 05
+" Last Change: 2022 Jan 05
if exists("b:current_syntax")
finish
endif
+scriptencoding utf-8
+
syn case match
syn sync minlines=50
+syn sync linebreaks=1
if has("spell")
syn spell toplevel
endif
syn include @gitcommitDiff syntax/diff.vim
-syn region gitcommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|#\)\@=/ fold contains=@gitcommitDiff
+syn region gitcommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@gitcommitDiff
syn match gitcommitSummary "^.*\%<51v." contained containedin=gitcommitFirstLine nextgroup=gitcommitOverflow contains=@Spell
syn match gitcommitOverflow ".*" contained contains=@Spell
-syn match gitcommitBlank "^[^#].*" contained contains=@Spell
+syn match gitcommitBlank "^.\+" contained contains=@Spell
+syn match gitcommitFirstLine "\%^.*" nextgroup=gitcommitBlank,gitcommitComment skipnl
+
+let s:scissors = 0
+let s:l = search('^[#;@!$%^&|:] -\{24,\} >8 -\{24,\}$', 'cnW', '', 100)
+if s:l == 0
+ let s:l = line('$')
+elseif getline(s:l)[0] !=# getline(s:l - 1)[0]
+ let s:scissors = 1
+endif
+let s:comment = escape((matchstr(getline(s:l), '^[#;@!$%^&|:]\S\@!') . '#')[0], '^$.*[]~\"/')
-if get(g:, "gitcommit_cleanup") is# "scissors"
- syn match gitcommitFirstLine "\%^.*" nextgroup=gitcommitBlank skipnl
- syn region gitcommitComment start=/^# -\+ >8 -\+$/ end=/\%$/ contains=gitcommitDiff
+if s:scissors
+ let s:comment .= ' -\{24,\} >8 -\{24,\}$'
+ exe 'syn region gitcommitComment start="^' . s:comment . '" end="\%$" contains=gitcommitDiff'
else
- syn match gitcommitFirstLine "\%^[^#].*" nextgroup=gitcommitBlank skipnl
- syn match gitcommitComment "^#.*"
+ exe 'syn match gitcommitComment "^' . s:comment . '.*"'
endif
+exe 'syn match gitcommitTrailers "\n\@<=\n\%([[:alnum:]-]\+\s*:.*\|(cherry picked from commit .*\)\%(\n\s.*\|\n[[:alnum:]-]\+\s*:.*\|\n(cherry picked from commit .*\)*\%(\n\n*\%(' . s:comment . '\)\|\n*\%$\)\@="'
-syn match gitcommitHead "^\%(# .*\n\)\+#$" contained transparent
-syn match gitcommitOnBranch "\%(^# \)\@<=On branch" contained containedin=gitcommitComment nextgroup=gitcommitBranch skipwhite
-syn match gitcommitOnBranch "\%(^# \)\@<=Your branch .\{-\} '" contained containedin=gitcommitComment nextgroup=gitcommitBranch skipwhite
+unlet s:l s:comment s:scissors
+
+syn match gitcommitTrailerToken "^[[:alnum:]-]\+\s*:" contained containedin=gitcommitTrailers
+
+syn match gitcommitHash "\<\x\{40,}\>" contains=@NoSpell display
+syn match gitcommitOnBranch "\%(^. \)\@<=On branch" contained containedin=gitcommitComment nextgroup=gitcommitBranch skipwhite
+syn match gitcommitOnBranch "\%(^. \)\@<=Your branch .\{-\} '" contained containedin=gitcommitComment nextgroup=gitcommitBranch skipwhite
syn match gitcommitBranch "[^ ']\+" contained
-syn match gitcommitNoBranch "\%(^# \)\@<=Not currently on any branch." contained containedin=gitcommitComment
-syn match gitcommitHeader "\%(^# \)\@<=.*:$" contained containedin=gitcommitComment
-syn region gitcommitAuthor matchgroup=gitCommitHeader start=/\%(^# \)\@<=\%(Author\|Committer\):/ end=/$/ keepend oneline contained containedin=gitcommitComment transparent
-syn match gitcommitNoChanges "\%(^# \)\@<=No changes$" contained containedin=gitcommitComment
+syn match gitcommitNoBranch "\%(^. \)\@<=Not currently on any branch." contained containedin=gitcommitComment
+syn match gitcommitHeader "\%(^. \)\@<=\S.*[::]\%(\n^$\)\@!$" contained containedin=gitcommitComment
+syn region gitcommitAuthor matchgroup=gitCommitHeader start=/\%(^. \)\@<=\%(Author\|Committer\|Date\):/ end=/$/ keepend oneline contained containedin=gitcommitComment transparent
+syn match gitcommitHeader "\%(^. \)\@<=commit\%( \x\{40,\}$\)\@=" contained containedin=gitcommitComment nextgroup=gitcommitHash skipwhite
+syn match gitcommitNoChanges "\%(^. \)\@<=No changes$" contained containedin=gitcommitComment
-syn region gitcommitUntracked start=/^# Untracked files:/ end=/^#$\|^#\@!/ contains=gitcommitHeader,gitcommitHead,gitcommitUntrackedFile fold
-syn match gitcommitUntrackedFile "\t\@<=.*" contained
+syn match gitcommitType "\%(^.\t\)\@<=[^[:punct:][:space:]][^/::]*[^[:punct:][:space:]][::]\ze "he=e-1 contained containedin=gitcommitComment nextgroup=gitcommitFile skipwhite
+syn match gitcommitFile ".\{-\}\%($\| -> \)\@=" contained nextgroup=gitcommitArrow
+syn match gitcommitArrow " -> " contained nextgroup=gitcommitFile
+syn match gitcommitUntrackedFile "\%(^.\t\)\@<=[^::/]*\%(/.*\)\=$" contained containedin=gitcommitComment
-syn region gitcommitDiscarded start=/^# Change\%(s not staged for commit\|d but not updated\):/ end=/^#$\|^#\@!/ contains=gitcommitHeader,gitcommitHead,gitcommitDiscardedType fold
-syn region gitcommitSelected start=/^# Changes to be committed:/ end=/^#$\|^#\@!/ contains=gitcommitHeader,gitcommitHead,gitcommitSelectedType fold
-syn region gitcommitUnmerged start=/^# Unmerged paths:/ end=/^#$\|^#\@!/ contains=gitcommitHeader,gitcommitHead,gitcommitUnmergedType fold
+syn region gitcommitUntracked start=/^\z(.\) Untracked files:$/ end=/^\z1\=$\|^\z1\@!/ contains=gitcommitHeader containedin=gitcommitComment containedin=gitcommitComment contained transparent fold
+syn region gitcommitDiscarded start=/^\z(.\) Change\%(s not staged for commit\|d but not updated\):$/ end=/^\z1\=$\|^\z1\@!/ contains=gitcommitHeader,gitcommitDiscardedType containedin=gitcommitComment containedin=gitcommitComment contained transparent fold
+syn region gitcommitSelected start=/^\z(.\) Changes to be committed:$/ end=/^\z1$\|^\z1\@!/ contains=gitcommitHeader,gitcommitSelectedType containedin=gitcommitComment containedin=gitcommitComment contained transparent fold
+syn region gitcommitUnmerged start=/^\z(.\) Unmerged paths:$/ end=/^\z1\=$\|^\z1\@!/ contains=gitcommitHeader,gitcommitUnmergedType containedin=gitcommitComment containedin=gitcommitComment contained transparent fold
+syn match gitcommitUntrackedFile "\%(^.\t\)\@<=.*" contained containedin=gitcommitUntracked
-syn match gitcommitDiscardedType "\t\@<=[[:lower:]][^:]*[[:lower:]]: "he=e-2 contained containedin=gitcommitComment nextgroup=gitcommitDiscardedFile skipwhite
-syn match gitcommitSelectedType "\t\@<=[[:lower:]][^:]*[[:lower:]]: "he=e-2 contained containedin=gitcommitComment nextgroup=gitcommitSelectedFile skipwhite
-syn match gitcommitUnmergedType "\t\@<=[[:lower:]][^:]*[[:lower:]]: "he=e-2 contained containedin=gitcommitComment nextgroup=gitcommitUnmergedFile skipwhite
-syn match gitcommitDiscardedFile ".\{-\}\%($\| -> \)\@=" contained nextgroup=gitcommitDiscardedArrow
-syn match gitcommitSelectedFile ".\{-\}\%($\| -> \)\@=" contained nextgroup=gitcommitSelectedArrow
-syn match gitcommitUnmergedFile ".\{-\}\%($\| -> \)\@=" contained nextgroup=gitcommitSelectedArrow
+syn match gitcommitDiscardedType "\%(^.\t\)\@<=[^[:punct:][:space:]][^/::]*[^[:punct:][:space:]][::]\ze "he=e-1 contained nextgroup=gitcommitDiscardedFile skipwhite
+syn match gitcommitSelectedType "\%(^.\t\)\@<=[^[:punct:][:space:]][^/::]*[^[:punct:][:space:]][::]\ze "he=e-1 contained nextgroup=gitcommitSelectedFile skipwhite
+syn match gitcommitUnmergedType "\%(^.\t\)\@<=[^[:punct:][:space:]][^/::]*[^[:punct:][:space:]][::]\ze "he=e-1 contained nextgroup=gitcommitUnmergedFile skipwhite
+syn match gitcommitDiscardedFile "\S.\{-\}\%($\| -> \)\@=" contained nextgroup=gitcommitDiscardedArrow
+syn match gitcommitSelectedFile "\S.\{-\}\%($\| -> \)\@=" contained nextgroup=gitcommitSelectedArrow
+syn match gitcommitUnmergedFile "\S.\{-\}\%($\| -> \)\@=" contained nextgroup=gitcommitUnmergedArrow
syn match gitcommitDiscardedArrow " -> " contained nextgroup=gitcommitDiscardedFile
syn match gitcommitSelectedArrow " -> " contained nextgroup=gitcommitSelectedFile
-syn match gitcommitUnmergedArrow " -> " contained nextgroup=gitcommitSelectedFile
-
-syn match gitcommitWarning "\%^[^#].*: needs merge$" nextgroup=gitcommitWarning skipnl
-syn match gitcommitWarning "^[^#].*: needs merge$" nextgroup=gitcommitWarning skipnl contained
-syn match gitcommitWarning "^\%(no changes added to commit\|nothing \%(added \)\=to commit\)\>.*\%$"
+syn match gitcommitUnmergedArrow " -> " contained nextgroup=gitcommitUnmergedFile
hi def link gitcommitSummary Keyword
+hi def link gitcommitTrailerToken Label
hi def link gitcommitComment Comment
-hi def link gitcommitUntracked gitcommitComment
-hi def link gitcommitDiscarded gitcommitComment
-hi def link gitcommitSelected gitcommitComment
-hi def link gitcommitUnmerged gitcommitComment
+hi def link gitcommitHash Identifier
hi def link gitcommitOnBranch Comment
hi def link gitcommitBranch Special
hi def link gitcommitNoBranch gitCommitBranch
diff --git a/runtime/syntax/gitrebase.vim b/runtime/syntax/gitrebase.vim
index bc6f34d1a7..13f157b005 100644
--- a/runtime/syntax/gitrebase.vim
+++ b/runtime/syntax/gitrebase.vim
@@ -2,7 +2,7 @@
" Language: git rebase --interactive
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" Filenames: git-rebase-todo
-" Last Change: 2019 Dec 06
+" Last Change: 2022 Jan 05
if exists("b:current_syntax")
finish
@@ -10,8 +10,10 @@ endif
syn case match
-syn match gitrebaseHash "\v<\x{7,}>" contained
-syn match gitrebaseCommit "\v<\x{7,}>" nextgroup=gitrebaseSummary skipwhite
+let s:c = escape((matchstr(getline('$'), '^[#;@!$%^&|:]\S\@!') . '#')[0], '^$.*[]~\"/')
+
+syn match gitrebaseHash "\v<\x{7,}>" contained contains=@NoSpell
+syn match gitrebaseCommit "\v<\x{7,}>" nextgroup=gitrebaseSummary skipwhite contains=@NoSpell
syn match gitrebasePick "\v^p%(ick)=>" nextgroup=gitrebaseCommit skipwhite
syn match gitrebaseReword "\v^r%(eword)=>" nextgroup=gitrebaseCommit skipwhite
syn match gitrebaseEdit "\v^e%(dit)=>" nextgroup=gitrebaseCommit skipwhite
@@ -26,12 +28,15 @@ syn match gitrebaseLabel "\v^l(abel)=>" nextgroup=gitrebaseName skipwhite
syn match gitrebaseReset "\v^(t|reset)=>" nextgroup=gitrebaseName skipwhite
syn match gitrebaseSummary ".*" contains=gitrebaseHash contained
syn match gitrebaseCommand ".*" contained
-syn match gitrebaseComment "^\s*#.*" contains=gitrebaseHash
+exe 'syn match gitrebaseComment " \@<=' . s:c . ' empty$" containedin=gitrebaseSummary contained'
+exe 'syn match gitrebaseComment "^\s*' . s:c . '.*" contains=gitrebaseHash'
syn match gitrebaseSquashError "\v%^%(s%(quash)=>|f%(ixup)=>)" nextgroup=gitrebaseCommit skipwhite
syn match gitrebaseMergeOption "\v-[Cc]>" nextgroup=gitrebaseMergeCommit skipwhite contained
syn match gitrebaseMergeCommit "\v<\x{7,}>" nextgroup=gitrebaseName skipwhite contained
syn match gitrebaseName "\v[^[:space:].*?i:^~/-]\S+" nextgroup=gitrebaseMergeComment skipwhite contained
-syn match gitrebaseMergeComment "#" nextgroup=gitrebaseSummary skipwhite contained
+exe 'syn match gitrebaseMergeComment "' . s:c . '" nextgroup=gitrebaseSummary skipwhite contained'
+
+unlet s:c
hi def link gitrebaseCommit gitrebaseHash
hi def link gitrebaseHash Identifier
diff --git a/runtime/syntax/i3config.vim b/runtime/syntax/i3config.vim
index a8b6637140..a2f50e50b8 100644
--- a/runtime/syntax/i3config.vim
+++ b/runtime/syntax/i3config.vim
@@ -1,8 +1,9 @@
" Vim syntax file
" Language: i3 config file
-" Maintainer: Mohamed Boughaba <mohamed dot bgb at gmail dot com>
+" Original Author: Mohamed Boughaba <mohamed dot bgb at gmail dot com>
+" Maintainer: Quentin Hibon (github user hiqua)
" Version: 0.4
-" Last Change: 2021 Dec 14
+" Last Change: 2022 Jan 15
" References:
" http://i3wm.org/docs/userguide.html#configuring
@@ -174,7 +175,7 @@ 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 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_min_width 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
diff --git a/runtime/syntax/rc.vim b/runtime/syntax/rc.vim
index 4c6856bc83..d69edd00fd 100644
--- a/runtime/syntax/rc.vim
+++ b/runtime/syntax/rc.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: M$ Resource files (*.rc)
" Maintainer: Christian Brabandt
-" Last Change: 2015-05-29
+" Last Change: 20220116
" Repository: https://github.com/chrisbra/vim-rc-syntax
" License: Vim (see :h license)
" Previous Maintainer: Heiko Erhardt <Heiko.Erhardt@munich.netsurf.de>
@@ -173,16 +173,17 @@ hi def link rcAttribute rcCommonAttribute
hi def link rcStdId rcStatement
hi def link rcStatement Statement
-" Default color overrides
-hi def rcLanguage term=reverse ctermbg=Red ctermfg=Yellow guibg=Red guifg=Yellow
-hi def rcMainObject term=underline ctermfg=Blue guifg=Blue
-hi def rcSubObject ctermfg=Green guifg=Green
-hi def rcCaptionParam term=underline ctermfg=DarkGreen guifg=Green
-hi def rcParam ctermfg=DarkGreen guifg=DarkGreen
-hi def rcStatement ctermfg=DarkGreen guifg=DarkGreen
-hi def rcCommonAttribute ctermfg=Brown guifg=Brown
+hi def link rcLanguage Constant
+hi def link rcCaptionParam Constant
+hi def link rcCommonAttribute Constant
+
+hi def link rcMainObject Identifier
+hi def link rcSubObject Define
+hi def link rcParam Constant
+hi def link rcStatement Statement
+"
+"hi def link rcIdentifier Identifier
-"hi def link rcIdentifier Identifier
let b:current_syntax = "rc"
diff --git a/runtime/syntax/scala.vim b/runtime/syntax/scala.vim
index 16e114778d..c08e60e55a 100644
--- a/runtime/syntax/scala.vim
+++ b/runtime/syntax/scala.vim
@@ -3,7 +3,7 @@
" Maintainer: Derek Wyatt
" URL: https://github.com/derekwyatt/vim-scala
" License: Same as Vim
-" Last Change: 23 August 2021
+" Last Change: 23 January 2022
" ----------------------------------------------------------------------------
if !exists('main_syntax')
@@ -43,55 +43,55 @@ syn keyword scalaKeyword class trait object extends with nextgroup=scalaInstance
syn keyword scalaKeyword case nextgroup=scalaKeyword,scalaCaseFollowing skipwhite
syn keyword scalaKeyword val nextgroup=scalaNameDefinition,scalaQuasiQuotes skipwhite
syn keyword scalaKeyword def var nextgroup=scalaNameDefinition skipwhite
-hi link scalaKeyword Keyword
+hi def link scalaKeyword Keyword
exe 'syn region scalaBlock start=/{/ end=/}/ contains=' . s:ContainedGroup() . ' fold'
syn keyword scalaAkkaSpecialWord when goto using startWith initialize onTransition stay become unbecome
-hi link scalaAkkaSpecialWord PreProc
+hi def link scalaAkkaSpecialWord PreProc
syn keyword scalatestSpecialWord shouldBe
syn match scalatestShouldDSLA /^\s\+\zsit should/
syn match scalatestShouldDSLB /\<should\>/
-hi link scalatestSpecialWord PreProc
-hi link scalatestShouldDSLA PreProc
-hi link scalatestShouldDSLB PreProc
+hi def link scalatestSpecialWord PreProc
+hi def link scalatestShouldDSLA PreProc
+hi def link scalatestShouldDSLB PreProc
syn match scalaSymbol /'[_A-Za-z0-9$]\+/
-hi link scalaSymbol Number
+hi def link scalaSymbol Number
syn match scalaChar /'.'/
syn match scalaChar /'\\[\\"'ntbrf]'/ contains=scalaEscapedChar
syn match scalaChar /'\\u[A-Fa-f0-9]\{4}'/ contains=scalaUnicodeChar
syn match scalaEscapedChar /\\[\\"'ntbrf]/
syn match scalaUnicodeChar /\\u[A-Fa-f0-9]\{4}/
-hi link scalaChar Character
-hi link scalaEscapedChar Special
-hi link scalaUnicodeChar Special
+hi def link scalaChar Character
+hi def link scalaEscapedChar Special
+hi def link scalaUnicodeChar Special
syn match scalaOperator "||"
syn match scalaOperator "&&"
syn match scalaOperator "|"
syn match scalaOperator "&"
-hi link scalaOperator Special
+hi def link scalaOperator Special
syn match scalaNameDefinition /\<[_A-Za-z0-9$]\+\>/ contained nextgroup=scalaPostNameDefinition,scalaVariableDeclarationList
syn match scalaNameDefinition /`[^`]\+`/ contained nextgroup=scalaPostNameDefinition
syn match scalaVariableDeclarationList /\s*,\s*/ contained nextgroup=scalaNameDefinition
syn match scalaPostNameDefinition /\_s*:\_s*/ contained nextgroup=scalaTypeDeclaration
-hi link scalaNameDefinition Function
+hi def link scalaNameDefinition Function
syn match scalaInstanceDeclaration /\<[_\.A-Za-z0-9$]\+\>/ contained nextgroup=scalaInstanceHash
syn match scalaInstanceDeclaration /`[^`]\+`/ contained
syn match scalaInstanceHash /#/ contained nextgroup=scalaInstanceDeclaration
-hi link scalaInstanceDeclaration Special
-hi link scalaInstanceHash Type
+hi def link scalaInstanceDeclaration Special
+hi def link scalaInstanceHash Type
syn match scalaUnimplemented /???/
-hi link scalaUnimplemented ERROR
+hi def link scalaUnimplemented ERROR
syn match scalaCapitalWord /\<[A-Z][A-Za-z0-9$]*\>/
-hi link scalaCapitalWord Special
+hi def link scalaCapitalWord Special
" Handle type declarations specially
syn region scalaTypeStatement matchgroup=Keyword start=/\<type\_s\+\ze/ end=/$/ contains=scalaTypeTypeDeclaration,scalaSquareBrackets,scalaTypeTypeEquals,scalaTypeStatement
@@ -105,18 +105,18 @@ syn match scalaTypeTypeEquals /=\ze[^>]/ contained nextgroup=scalaTypeTypePostDe
syn match scalaTypeTypeExtension /)\?\_s*\zs\%(⇒\|=>\|<:\|:>\|=:=\|::\|#\)/ contained contains=scalaTypeOperator nextgroup=scalaTypeTypeDeclaration skipwhite
syn match scalaTypeTypePostDeclaration /\<[_\.A-Za-z0-9$]\+\>/ contained nextgroup=scalaTypeTypePostExtension skipwhite
syn match scalaTypeTypePostExtension /\%(⇒\|=>\|<:\|:>\|=:=\|::\)/ contained contains=scalaTypeOperator nextgroup=scalaTypeTypePostDeclaration skipwhite
-hi link scalaTypeTypeDeclaration Type
-hi link scalaTypeTypeExtension Keyword
-hi link scalaTypeTypePostDeclaration Special
-hi link scalaTypeTypePostExtension Keyword
+hi def link scalaTypeTypeDeclaration Type
+hi def link scalaTypeTypeExtension Keyword
+hi def link scalaTypeTypePostDeclaration Special
+hi def link scalaTypeTypePostExtension Keyword
syn match scalaTypeDeclaration /(/ contained nextgroup=scalaTypeExtension contains=scalaRoundBrackets skipwhite
syn match scalaTypeDeclaration /\%(⇒\|=>\)\ze/ contained nextgroup=scalaTypeDeclaration contains=scalaTypeExtension skipwhite
syn match scalaTypeDeclaration /\<[_\.A-Za-z0-9$]\+\>/ contained nextgroup=scalaTypeExtension skipwhite
syn match scalaTypeExtension /)\?\_s*\zs\%(⇒\|=>\|<:\|:>\|=:=\|::\|#\)/ contained contains=scalaTypeOperator nextgroup=scalaTypeDeclaration skipwhite
-hi link scalaTypeDeclaration Type
-hi link scalaTypeExtension Keyword
-hi link scalaTypePostExtension Keyword
+hi def link scalaTypeDeclaration Type
+hi def link scalaTypeExtension Keyword
+hi def link scalaTypePostExtension Keyword
syn match scalaTypeAnnotation /\%([_a-zA-Z0-9$\s]:\_s*\)\ze[_=(\.A-Za-z0-9$]\+/ skipwhite nextgroup=scalaTypeDeclaration contains=scalaRoundBrackets
syn match scalaTypeAnnotation /)\_s*:\_s*\ze[_=(\.A-Za-z0-9$]\+/ skipwhite nextgroup=scalaTypeDeclaration
@@ -124,51 +124,51 @@ hi clear scalaTypeAnnotation
syn match scalaCaseFollowing /\<[_\.A-Za-z0-9$]\+\>/ contained contains=scalaCapitalWord
syn match scalaCaseFollowing /`[^`]\+`/ contained contains=scalaCapitalWord
-hi link scalaCaseFollowing Special
+hi def link scalaCaseFollowing Special
syn keyword scalaKeywordModifier abstract override final lazy implicit private protected sealed null super
syn keyword scalaSpecialFunction implicitly require
-hi link scalaKeywordModifier Function
-hi link scalaSpecialFunction Function
+hi def link scalaKeywordModifier Function
+hi def link scalaSpecialFunction Function
syn keyword scalaSpecial this true false ne eq
syn keyword scalaSpecial new nextgroup=scalaInstanceDeclaration skipwhite
syn match scalaSpecial "\%(=>\|⇒\|<-\|←\|->\|→\)"
syn match scalaSpecial /`[^`]\+`/ " Backtick literals
-hi link scalaSpecial PreProc
+hi def link scalaSpecial PreProc
syn keyword scalaExternal package import
-hi link scalaExternal Include
+hi def link scalaExternal Include
syn match scalaStringEmbeddedQuote /\\"/ contained
syn region scalaString start=/"/ end=/"/ contains=scalaStringEmbeddedQuote,scalaEscapedChar,scalaUnicodeChar
-hi link scalaString String
-hi link scalaStringEmbeddedQuote String
+hi def link scalaString String
+hi def link scalaStringEmbeddedQuote String
syn region scalaIString matchgroup=scalaInterpolationBrackets start=/\<[a-zA-Z][a-zA-Z0-9_]*"/ skip=/\\"/ end=/"/ contains=scalaInterpolation,scalaInterpolationB,scalaEscapedChar,scalaUnicodeChar
syn region scalaTripleIString matchgroup=scalaInterpolationBrackets start=/\<[a-zA-Z][a-zA-Z0-9_]*"""/ end=/"""\ze\%([^"]\|$\)/ contains=scalaInterpolation,scalaInterpolationB,scalaEscapedChar,scalaUnicodeChar
-hi link scalaIString String
-hi link scalaTripleIString String
+hi def link scalaIString String
+hi def link scalaTripleIString String
syn match scalaInterpolation /\$[a-zA-Z0-9_$]\+/ contained
exe 'syn region scalaInterpolationB matchgroup=scalaInterpolationBoundary start=/\${/ end=/}/ contained contains=' . s:ContainedGroup()
-hi link scalaInterpolation Function
+hi def link scalaInterpolation Function
hi clear scalaInterpolationB
syn region scalaFString matchgroup=scalaInterpolationBrackets start=/f"/ skip=/\\"/ end=/"/ contains=scalaFInterpolation,scalaFInterpolationB,scalaEscapedChar,scalaUnicodeChar
syn match scalaFInterpolation /\$[a-zA-Z0-9_$]\+\(%[-A-Za-z0-9\.]\+\)\?/ contained
exe 'syn region scalaFInterpolationB matchgroup=scalaInterpolationBoundary start=/${/ end=/}\(%[-A-Za-z0-9\.]\+\)\?/ contained contains=' . s:ContainedGroup()
-hi link scalaFString String
-hi link scalaFInterpolation Function
+hi def link scalaFString String
+hi def link scalaFInterpolation Function
hi clear scalaFInterpolationB
syn region scalaTripleString start=/"""/ end=/"""\%([^"]\|$\)/ contains=scalaEscapedChar,scalaUnicodeChar
syn region scalaTripleFString matchgroup=scalaInterpolationBrackets start=/f"""/ end=/"""\%([^"]\|$\)/ contains=scalaFInterpolation,scalaFInterpolationB,scalaEscapedChar,scalaUnicodeChar
-hi link scalaTripleString String
-hi link scalaTripleFString String
+hi def link scalaTripleString String
+hi def link scalaTripleFString String
-hi link scalaInterpolationBrackets Special
-hi link scalaInterpolationBoundary Function
+hi def link scalaInterpolationBrackets Special
+hi def link scalaInterpolationBoundary Function
syn match scalaNumber /\<0[dDfFlL]\?\>/ " Just a bare 0
syn match scalaNumber /\<[1-9]\d*[dDfFlL]\?\>/ " A multi-digit number - octal numbers with leading 0's are deprecated in Scala
@@ -176,16 +176,16 @@ syn match scalaNumber /\<0[xX][0-9a-fA-F]\+[dDfFlL]\?\>/ " Hex number
syn match scalaNumber /\%(\<\d\+\.\d*\|\.\d\+\)\%([eE][-+]\=\d\+\)\=[fFdD]\=/ " exponential notation 1
syn match scalaNumber /\<\d\+[eE][-+]\=\d\+[fFdD]\=\>/ " exponential notation 2
syn match scalaNumber /\<\d\+\%([eE][-+]\=\d\+\)\=[fFdD]\>/ " exponential notation 3
-hi link scalaNumber Number
+hi def link scalaNumber Number
syn region scalaRoundBrackets start="(" end=")" skipwhite contained contains=scalaTypeDeclaration,scalaSquareBrackets,scalaRoundBrackets
syn region scalaSquareBrackets matchgroup=scalaSquareBracketsBrackets start="\[" end="\]" skipwhite nextgroup=scalaTypeExtension contains=scalaTypeDeclaration,scalaSquareBrackets,scalaTypeOperator,scalaTypeAnnotationParameter
syn match scalaTypeOperator /[-+=:<>]\+/ contained
syn match scalaTypeAnnotationParameter /@\<[`_A-Za-z0-9$]\+\>/ contained
-hi link scalaSquareBracketsBrackets Type
-hi link scalaTypeOperator Keyword
-hi link scalaTypeAnnotationParameter Function
+hi def link scalaSquareBracketsBrackets Type
+hi def link scalaTypeOperator Keyword
+hi def link scalaTypeAnnotationParameter Function
syn match scalaShebang "\%^#!.*" display
syn region scalaMultilineComment start="/\*" end="\*/" contains=scalaMultilineComment,scalaDocLinks,scalaParameterAnnotation,scalaCommentAnnotation,scalaTodo,scalaCommentCodeBlock,@Spell keepend fold
@@ -195,20 +195,20 @@ syn match scalaParamAnnotationValue /[.`_A-Za-z0-9$]\+/ contained
syn region scalaDocLinks start="\[\[" end="\]\]" contained
syn region scalaCommentCodeBlock matchgroup=Keyword start="{{{" end="}}}" contained
syn match scalaTodo "\vTODO|FIXME|XXX" contained
-hi link scalaShebang Comment
-hi link scalaMultilineComment Comment
-hi link scalaDocLinks Function
-hi link scalaParameterAnnotation Function
-hi link scalaParamAnnotationValue Keyword
-hi link scalaCommentAnnotation Function
-hi link scalaCommentCodeBlock String
-hi link scalaTodo Todo
+hi def link scalaShebang Comment
+hi def link scalaMultilineComment Comment
+hi def link scalaDocLinks Function
+hi def link scalaParameterAnnotation Function
+hi def link scalaParamAnnotationValue Keyword
+hi def link scalaCommentAnnotation Function
+hi def link scalaCommentCodeBlock String
+hi def link scalaTodo Todo
syn match scalaAnnotation /@\<[`_A-Za-z0-9$]\+\>/
-hi link scalaAnnotation PreProc
+hi def link scalaAnnotation PreProc
syn match scalaTrailingComment "//.*$" contains=scalaTodo,@Spell
-hi link scalaTrailingComment Comment
+hi def link scalaTrailingComment Comment
syn match scalaAkkaFSM /goto([^)]*)\_s\+\<using\>/ contains=scalaAkkaFSMGotoUsing
syn match scalaAkkaFSM /stay\_s\+using/
@@ -221,8 +221,8 @@ syn match scalaAkkaFSM /onTermination/
syn match scalaAkkaFSM /whenUnhandled/
syn match scalaAkkaFSMGotoUsing /\<using\>/
syn match scalaAkkaFSMGotoUsing /\<goto\>/
-hi link scalaAkkaFSM PreProc
-hi link scalaAkkaFSMGotoUsing PreProc
+hi def link scalaAkkaFSM PreProc
+hi def link scalaAkkaFSMGotoUsing PreProc
let b:current_syntax = 'scala'
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 76b699800e..18243fec2b 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -130,7 +130,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
return 0;
}
- uint64_t ns_id = src2ns(&src_id);
+ uint32_t ns_id = src2ns(&src_id);
int width;
VirtText virt_text = parse_virt_text(chunks, err, &width);
@@ -148,11 +148,12 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
return src_id;
}
- Decoration *decor = xcalloc(1, sizeof(*decor));
- decor->virt_text = virt_text;
- decor->virt_text_width = width;
+ Decoration decor = DECORATION_INIT;
+ decor.virt_text = virt_text;
+ decor.virt_text_width = width;
+ decor.priority = 0;
- extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, true,
+ extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, &decor, true,
false, kExtmarkNoUndo);
return src_id;
}
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 742b953c2a..3a968f07ab 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -85,12 +85,12 @@ const char *describe_ns(NS ns_id)
}
// Is the Namespace in use?
-static bool ns_initialized(uint64_t ns)
+static bool ns_initialized(uint32_t ns)
{
if (ns < 1) {
return false;
}
- return ns < (uint64_t)next_namespace_id;
+ return ns < (uint32_t)next_namespace_id;
}
@@ -106,22 +106,55 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict)
if (add_dict) {
Dictionary dict = ARRAY_DICT_INIT;
+ PUT(dict, "right_gravity", BOOLEAN_OBJ(extmark.right_gravity));
+
if (extmark.end_row >= 0) {
PUT(dict, "end_row", INTEGER_OBJ(extmark.end_row));
PUT(dict, "end_col", INTEGER_OBJ(extmark.end_col));
+ PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity));
}
- if (extmark.decor) {
- Decoration *decor = extmark.decor;
- if (decor->hl_id) {
- String name = cstr_to_string((const char *)syn_id2name(decor->hl_id));
- PUT(dict, "hl_group", STRING_OBJ(name));
+ Decoration *decor = &extmark.decor;
+ if (decor->hl_id) {
+ String name = cstr_to_string((const char *)syn_id2name(decor->hl_id));
+ PUT(dict, "hl_group", STRING_OBJ(name));
+ PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol));
+ }
+ if (decor->hl_mode) {
+ PUT(dict, "hl_mode", STRING_OBJ(cstr_to_string(hl_mode_str[decor->hl_mode])));
+ }
+
+ if (kv_size(decor->virt_text)) {
+ Array chunks = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < decor->virt_text.size; i++) {
+ Array chunk = ARRAY_DICT_INIT;
+ VirtTextChunk *vtc = &decor->virt_text.items[i];
+ ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text)));
+ if (vtc->hl_id > 0) {
+ ADD(chunk,
+ STRING_OBJ(cstr_to_string((const char *)syn_id2name(vtc->hl_id))));
+ }
+ ADD(chunks, ARRAY_OBJ(chunk));
}
- if (kv_size(decor->virt_text)) {
+ PUT(dict, "virt_text", ARRAY_OBJ(chunks));
+ PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide));
+ if (decor->virt_text_pos == kVTWinCol) {
+ PUT(dict, "virt_text_win_col", INTEGER_OBJ(decor->col));
+ }
+ PUT(dict, "virt_text_pos",
+ STRING_OBJ(cstr_to_string(virt_text_pos_str[decor->virt_text_pos])));
+ }
+
+ if (kv_size(decor->virt_lines)) {
+ Array all_chunks = ARRAY_DICT_INIT;
+ bool virt_lines_leftcol = false;
+ for (size_t i = 0; i < decor->virt_lines.size; i++) {
Array chunks = ARRAY_DICT_INIT;
- for (size_t i = 0; i < decor->virt_text.size; i++) {
+ VirtText *vt = &decor->virt_lines.items[i].line;
+ virt_lines_leftcol = decor->virt_lines.items[i].left_col;
+ for (size_t j = 0; j < vt->size; j++) {
Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &decor->virt_text.items[i];
+ VirtTextChunk *vtc = &vt->items[j];
ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text)));
if (vtc->hl_id > 0) {
ADD(chunk,
@@ -129,9 +162,14 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict)
}
ADD(chunks, ARRAY_OBJ(chunk));
}
- PUT(dict, "virt_text", ARRAY_OBJ(chunks));
+ ADD(all_chunks, ARRAY_OBJ(chunks));
}
+ PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks));
+ PUT(dict, "virt_lines_above", BOOLEAN_OBJ(decor->virt_lines_above));
+ PUT(dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol));
+ }
+ if (decor->hl_id || kv_size(decor->virt_text)) {
PUT(dict, "priority", INTEGER_OBJ(decor->priority));
}
@@ -166,7 +204,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return rv;
}
- if (!ns_initialized((uint64_t)ns_id)) {
+ if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
return rv;
}
@@ -191,7 +229,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
}
- ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
+ ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
if (extmark.row < 0) {
return rv;
}
@@ -252,7 +290,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
return rv;
}
- if (!ns_initialized((uint64_t)ns_id)) {
+ if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
return rv;
}
@@ -310,7 +348,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
}
- ExtmarkInfoArray marks = extmark_get(buf, (uint64_t)ns_id, l_row, l_col,
+ ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col,
u_row, u_col, (int64_t)limit, reverse);
for (size_t i = 0; i < kv_size(marks); i++) {
@@ -404,6 +442,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// for left). Defaults to false.
/// - priority: a priority value for the highlight group. For
/// example treesitter highlighting uses a value of 100.
+/// - strict: boolean that indicates extmark should not be placed
+/// if the line or column value is past the end of the
+/// buffer or end of the line respectively. Defaults to true.
+///
/// @param[out] err Error details, if any
/// @return Id of the created/updated extmark
Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col,
@@ -417,14 +459,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
- if (!ns_initialized((uint64_t)ns_id)) {
+ if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
goto error;
}
- uint64_t id = 0;
+ uint32_t id = 0;
if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) {
- id = (uint64_t)opts->id.data.integer;
+ id = (uint32_t)opts->id.data.integer;
} else if (HAS_KEY(opts->id)) {
api_set_error(err, kErrorTypeValidation, "id is not a positive integer");
goto error;
@@ -441,9 +483,18 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
opts->end_row = opts->end_line;
}
+#define OPTION_TO_BOOL(target, name, val) \
+ target = api_object_to_bool(opts->name, #name, val, err); \
+ if (ERROR_SET(err)) { \
+ goto error; \
+ }
+
+ bool strict = true;
+ OPTION_TO_BOOL(strict, strict, true);
+
if (opts->end_row.type == kObjectTypeInteger) {
Integer val = opts->end_row.data.integer;
- if (val < 0 || val > buf->b_ml.ml_line_count) {
+ if (val < 0 || (val > buf->b_ml.ml_line_count && strict)) {
api_set_error(err, kErrorTypeValidation, "end_row value outside range");
goto error;
} else {
@@ -512,12 +563,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
-#define OPTION_TO_BOOL(target, name, val) \
- target = api_object_to_bool(opts->name, #name, val, err); \
- if (ERROR_SET(err)) { \
- goto error; \
- }
-
OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
OPTION_TO_BOOL(decor.hl_eol, hl_eol, false);
@@ -596,16 +641,30 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
bool ephemeral = false;
OPTION_TO_BOOL(ephemeral, ephemeral, false);
- if (line < 0 || line > buf->b_ml.ml_line_count) {
+ if (line < 0) {
api_set_error(err, kErrorTypeValidation, "line value outside range");
goto error;
+ } else if (line > buf->b_ml.ml_line_count) {
+ if (strict) {
+ api_set_error(err, kErrorTypeValidation, "line value outside range");
+ goto error;
+ } else {
+ line = buf->b_ml.ml_line_count;
+ }
} else if (line < buf->b_ml.ml_line_count) {
len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line+1, false));
}
if (col == -1) {
col = (Integer)len;
- } else if (col < -1 || col > (Integer)len) {
+ } else if (col > (Integer)len) {
+ if (strict) {
+ api_set_error(err, kErrorTypeValidation, "col value outside range");
+ goto error;
+ } else {
+ col = (Integer)len;
+ }
+ } else if (col < -1) {
api_set_error(err, kErrorTypeValidation, "col value outside range");
goto error;
}
@@ -621,27 +680,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
line2 = (int)line;
}
if (col2 > (Integer)len) {
- api_set_error(err, kErrorTypeValidation, "end_col value outside range");
- goto error;
+ if (strict) {
+ api_set_error(err, kErrorTypeValidation, "end_col value outside range");
+ goto error;
+ } else {
+ col2 = (int)len;
+ }
}
} else if (line2 >= 0) {
col2 = 0;
}
- Decoration *d = NULL;
-
- if (ephemeral) {
- d = &decor;
- } else if (kv_size(decor.virt_text) || kv_size(decor.virt_lines)
- || decor.priority != DECOR_PRIORITY_BASE
- || decor.hl_eol) {
- // TODO(bfredl): this is a bit sketchy. eventually we should
- // have predefined decorations for both marks/ephemerals
- d = xcalloc(1, sizeof(*d));
- *d = decor;
- } else if (decor.hl_id) {
- d = decor_hl(decor.hl_id);
- }
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
@@ -652,12 +701,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
- extmark_set(buf, (uint64_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
- d, right_gravity, end_right_gravity, kExtmarkNoUndo);
-
- if (kv_size(decor.virt_lines)) {
- redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, line+1+(decor.virt_lines_above?0:1)));
- }
+ extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
+ &decor, right_gravity, end_right_gravity, kExtmarkNoUndo);
}
return (Integer)id;
@@ -682,23 +727,23 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
if (!buf) {
return false;
}
- if (!ns_initialized((uint64_t)ns_id)) {
+ if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
return false;
}
- return extmark_del(buf, (uint64_t)ns_id, (uint64_t)id);
+ return extmark_del(buf, (uint32_t)ns_id, (uint32_t)id);
}
-uint64_t src2ns(Integer *src_id)
+uint32_t src2ns(Integer *src_id)
{
if (*src_id == 0) {
*src_id = nvim_create_namespace((String)STRING_INIT);
}
if (*src_id < 0) {
- return UINT64_MAX;
+ return (((uint32_t)1) << 31) - 1;
} else {
- return (uint64_t)(*src_id);
+ return (uint32_t)(*src_id);
}
}
@@ -753,7 +798,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
col_end = MAXCOL;
}
- uint64_t ns = src2ns(&ns_id);
+ uint32_t ns = src2ns(&ns_id);
if (!(line < buf->b_ml.ml_line_count)) {
// safety check, we can't add marks outside the range
@@ -773,10 +818,13 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
end_line++;
}
+ Decoration decor = DECORATION_INIT;
+ decor.hl_id = hl_id;
+
extmark_set(buf, ns, NULL,
(int)line, (colnr_T)col_start,
end_line, (colnr_T)col_end,
- decor_hl(hl_id), true, false, kExtmarkNoUndo);
+ &decor, true, false, kExtmarkNoUndo);
return ns_id;
}
@@ -808,7 +856,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
if (line_end < 0 || line_end > MAXLNUM) {
line_end = MAXLNUM;
}
- extmark_clear(buf, (ns_id < 0 ? 0 : (uint64_t)ns_id),
+ extmark_clear(buf, (ns_id < 0 ? 0 : (uint32_t)ns_id),
(int)line_start, 0,
(int)line_end-1, MAXCOL);
}
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 97ee885ff6..7d521bbf25 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -21,6 +21,7 @@ return {
"virt_lines";
"virt_lines_above";
"virt_lines_leftcol";
+ "strict";
};
keymap = {
"noremap";
@@ -43,6 +44,7 @@ return {
"count";
"desc";
"force";
+ "keepscript";
"nargs";
"range";
"register";
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 36da6c13a9..3d4ff202fe 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -10,6 +10,9 @@
#include "nvim/api/private/helpers.h"
#include "nvim/assert.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/userfunc.h"
+#include "nvim/lua/converter.h"
+#include "nvim/lua/executor.h"
/// Helper structure for vim_to_object
typedef struct {
@@ -228,6 +231,14 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
/// @return The converted value
Object vim_to_object(typval_T *obj)
{
+ if (obj->v_type == VAR_FUNC) {
+ ufunc_T *fp = find_func(obj->vval.v_string);
+ assert(fp != NULL);
+ if (fp->uf_cb == nlua_CFunction_func_call) {
+ LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref);
+ return LUAREF_OBJ(ref);
+ }
+ }
EncodedData edata;
kvi_init(edata.stack);
const int evo_ret = encode_vim_to_object(&edata, obj,
@@ -340,6 +351,16 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
tv->vval.v_dict = dict;
break;
}
+
+ case kObjectTypeLuaRef: {
+ LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState));
+ state->lua_callable.func_ref = api_new_luaref(obj.data.luaref);
+ char_u *name = register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, state);
+ tv->v_type = VAR_FUNC;
+ tv->vval.v_string = vim_strsave(name);
+ break;
+ }
+
default:
abort();
}
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 38a82343c3..f540f8f7ee 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1141,7 +1141,7 @@ bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int
return false;
}
- ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
+ ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
if (extmark.row >= 0) {
*row = extmark.row;
*col = extmark.col;
@@ -1387,6 +1387,11 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int
LuaRef luaref = LUA_NOREF;
LuaRef compl_luaref = LUA_NOREF;
+ if (mb_islower(name.data[0])) {
+ api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter");
+ goto err;
+ }
+
if (HAS_KEY(opts->range) && HAS_KEY(opts->count)) {
api_set_error(err, kErrorTypeValidation, "'range' and 'count' are mutually exclusive");
goto err;
@@ -1505,6 +1510,12 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int
goto err;
}
+ if (api_object_to_bool(opts->keepscript, "keepscript", false, err)) {
+ argt |= EX_KEEPSCRIPT;
+ } else if (ERROR_SET(err)) {
+ goto err;
+ }
+
bool force = api_object_to_bool(opts->force, "force", true, err);
if (ERROR_SET(err)) {
goto err;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 59db12f2c0..7c194935ce 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -187,21 +187,23 @@ static void on_redraw_event(void **argv)
/// On execution error: does not fail, but updates v:errmsg.
///
/// To input sequences like <C-o> use |nvim_replace_termcodes()| (typically
-/// with escape_csi=true) to replace |keycodes|, then pass the result to
+/// with escape_ks=false) to replace |keycodes|, then pass the result to
/// nvim_feedkeys().
///
/// Example:
/// <pre>
/// :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
-/// :call nvim_feedkeys(key, 'n', v:true)
+/// :call nvim_feedkeys(key, 'n', v:false)
/// </pre>
///
/// @param keys to be typed
/// @param mode behavior flags, see |feedkeys()|
-/// @param escape_csi If true, escape K_SPECIAL/CSI bytes in `keys`
+/// @param escape_ks If true, escape K_SPECIAL bytes in `keys`
+/// This should be false if you already used
+/// |nvim_replace_termcodes()|, and true otherwise.
/// @see feedkeys()
-/// @see vim_strsave_escape_csi
-void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
+/// @see vim_strsave_escape_ks
+void nvim_feedkeys(String keys, String mode, Boolean escape_ks)
FUNC_API_SINCE(1)
{
bool remap = true;
@@ -232,10 +234,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
}
char *keys_esc;
- if (escape_csi) {
- // Need to escape K_SPECIAL and CSI before putting the string in the
+ if (escape_ks) {
+ // Need to escape K_SPECIAL before putting the string in the
// typeahead buffer.
- keys_esc = (char *)vim_strsave_escape_csi((char_u *)keys.data);
+ keys_esc = (char *)vim_strsave_escape_ks((char_u *)keys.data);
} else {
keys_esc = keys.data;
}
@@ -245,7 +247,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
typebuf_was_filled = true;
}
- if (escape_csi) {
+ if (escape_ks) {
xfree(keys_esc);
}
@@ -2281,6 +2283,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
fillchar = ' ';
} else {
wp = find_window_by_handle(window, err);
+
+ if (wp == NULL) {
+ api_set_error(err, kErrorTypeException, "unknown winid %d", window);
+ return result;
+ }
ewp = wp;
if (fillchar == 0) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index abd22fba26..eee5a0b46c 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1454,7 +1454,10 @@ void set_curbuf(buf_T *buf, int action)
}
if (bufref_valid(&prevbufref) && !aborting()) {
win_T *previouswin = curwin;
- if (prevbuf == curbuf) {
+ // Do not sync when in Insert mode and the buffer is open in
+ // another window, might be a timer doing something in another
+ // window.
+ if (prevbuf == curbuf && ((State & INSERT) == 0 || curbuf->b_nwindows <= 1)) {
u_sync(false);
}
close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL,
@@ -4001,14 +4004,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
case STL_VIRTCOL:
case STL_VIRTCOL_ALT: {
- // In list mode virtcol needs to be recomputed
- colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
- wp->w_p_list = false;
- getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = true;
- }
- virtcol++;
+ colnr_T virtcol = wp->w_virtcol + 1;
// Don't display %V if it's the same as %c.
if (opt == STL_VIRTCOL_ALT
&& (virtcol == (colnr_T)(!(State & INSERT) && empty_line
@@ -4351,7 +4347,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Only free the string buffer if we allocated it.
// Note: This is not needed if `str` is pointing at `tmp`
if (opt == STL_VIM_EXPR) {
- xfree(str);
+ XFREE_CLEAR(str);
}
if (num >= 0 || (!itemisflag && str && *str)) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 63a550c017..7b17c5b506 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -204,6 +204,10 @@ typedef struct {
#define w_p_nu w_onebuf_opt.wo_nu // 'number'
int wo_rnu;
#define w_p_rnu w_onebuf_opt.wo_rnu // 'relativenumber'
+ char_u *wo_ve;
+#define w_p_ve w_onebuf_opt.wo_ve // 'virtualedit'
+ unsigned wo_ve_flags;
+#define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit'
long wo_nuw;
#define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth'
int wo_wfh;
@@ -866,8 +870,7 @@ struct file_buffer {
int b_mapped_ctrl_c; // modes where CTRL-C is mapped
MarkTree b_marktree[1];
- Map(uint64_t, ExtmarkItem) b_extmark_index[1];
- Map(uint64_t, ExtmarkNs) b_extmark_ns[1]; // extmark namespaces
+ Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces
size_t b_virt_line_blocks; // number of virt_line blocks
// array of channel_id:s which have asked to receive updates for this
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 1dbbfff024..0c16b204e3 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -789,7 +789,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// fixpos is true, we don't want to end up positioned at the NUL,
// unless "restart_edit" is set or 'virtualedit' contains "onemore".
if (col > 0 && fixpos && restart_edit == 0
- && (ve_flags & VE_ONEMORE) == 0) {
+ && (get_ve_flags() & VE_ONEMORE) == 0) {
curwin->w_cursor.col--;
curwin->w_cursor.coladd = 0;
curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col);
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index 81b75e2d31..50d6b3600a 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -96,6 +96,8 @@ struct Channel {
EXTERN PMap(uint64_t) channels INIT(= MAP_INIT);
+EXTERN Callback on_print INIT(= CALLBACK_INIT);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "channel.h.generated.h"
#endif
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 6e2c6232d7..55f55a46b2 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -15,6 +15,7 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/move.h"
+#include "nvim/option.h"
#include "nvim/plines.h"
#include "nvim/screen.h"
#include "nvim/state.h"
@@ -110,7 +111,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
|| (State & TERM_FOCUS)
|| restart_edit != NUL
|| (VIsual_active && *p_sel != 'o')
- || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL);
+ || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL);
line = ml_get_buf(curbuf, pos->lnum, false);
if (wcol >= MAXCOL) {
@@ -366,6 +367,7 @@ void check_cursor_col_win(win_T *win)
colnr_T len;
colnr_T oldcol = win->w_cursor.col;
colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd;
+ unsigned int cur_ve_flags = get_ve_flags();
len = (colnr_T)STRLEN(ml_get_buf(win->w_buffer, win->w_cursor.lnum, false));
if (len == 0) {
@@ -377,7 +379,7 @@ void check_cursor_col_win(win_T *win)
* - 'virtualedit' is set */
if ((State & INSERT) || restart_edit
|| (VIsual_active && *p_sel != 'o')
- || (ve_flags & VE_ONEMORE)
+ || (cur_ve_flags & VE_ONEMORE)
|| virtual_active()) {
win->w_cursor.col = len;
} else {
@@ -394,7 +396,7 @@ void check_cursor_col_win(win_T *win)
// line.
if (oldcol == MAXCOL) {
win->w_cursor.coladd = 0;
- } else if (ve_flags == VE_ALL) {
+ } else if (cur_ve_flags == VE_ALL) {
if (oldcoladd > win->w_cursor.col) {
win->w_cursor.coladd = oldcoladd - win->w_cursor.col;
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index c0f3c32f93..935b233752 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -13,8 +13,6 @@
# include "decoration.c.generated.h"
#endif
-static PMap(uint64_t) hl_decors;
-
/// Add highlighting to a buffer, bounded by two cursor positions,
/// with an offset.
///
@@ -33,9 +31,9 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
{
colnr_T hl_start = 0;
colnr_T hl_end = 0;
- Decoration *decor = decor_hl(hl_id);
+ Decoration decor = DECORATION_INIT;
+ decor.hl_id = hl_id;
- decor->priority = DECOR_PRIORITY_BASE;
// TODO(bfredl): if decoration had blocky mode, we could avoid this loop
for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum++) {
int end_off = 0;
@@ -59,40 +57,23 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
hl_start = pos_start.col + offset;
hl_end = pos_end.col + offset;
}
- (void)extmark_set(buf, (uint64_t)src_id, NULL,
- (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end,
- decor, true, false, kExtmarkNoUndo);
- }
-}
-
-Decoration *decor_hl(int hl_id)
-{
- assert(hl_id > 0);
- Decoration **dp = (Decoration **)pmap_ref(uint64_t)(&hl_decors,
- (uint64_t)hl_id, true);
- if (*dp) {
- return *dp;
+ extmark_set(buf, (uint32_t)src_id, NULL,
+ (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end,
+ &decor, true, false, kExtmarkNoUndo);
}
-
- Decoration *decor = xcalloc(1, sizeof(*decor));
- decor->hl_id = hl_id;
- decor->shared = true;
- decor->priority = DECOR_PRIORITY_BASE;
- *dp = decor;
- return decor;
}
void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
{
- if (decor->hl_id && row2 >= row1) {
+ if ((!decor || decor->hl_id) && row2 >= row1) {
redraw_buf_range_later(buf, row1+1, row2+1);
}
- if (kv_size(decor->virt_text)) {
+ if (decor && kv_size(decor->virt_text)) {
redraw_buf_line_later(buf, row1+1);
}
- if (kv_size(decor->virt_lines)) {
+ if (decor && kv_size(decor->virt_lines)) {
redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count,
row1+1+(decor->virt_lines_above?0:1)));
}
@@ -100,17 +81,17 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
{
- if (kv_size(decor->virt_lines)) {
+ decor_redraw(buf, row, row2, decor);
+ if (decor && kv_size(decor->virt_lines)) {
assert(buf->b_virt_line_blocks > 0);
buf->b_virt_line_blocks--;
}
- decor_redraw(buf, row, row2, decor);
decor_free(decor);
}
void decor_free(Decoration *decor)
{
- if (decor && !decor->shared) {
+ if (decor) {
clear_virttext(&decor->virt_text);
for (size_t i = 0; i < kv_size(decor->virt_lines); i++) {
clear_virttext(&kv_A(decor->virt_lines, i).line);
@@ -134,17 +115,16 @@ Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, row, 0, itr);
while (true) {
- mtmark_t mark = marktree_itr_current(itr);
- if (mark.row < 0 || mark.row > row) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0 || mark.pos.row > row) {
break;
- } else if (marktree_decor_level(mark.id) < kDecorLevelVisible) {
+ } else if (marktree_decor_level(mark) < kDecorLevelVisible) {
goto next_mark;
}
- ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index,
- mark.id, false);
- if (item && (ns_id == 0 || ns_id == item->ns_id)
- && item->decor && kv_size(item->decor->virt_text)) {
- return item->decor;
+ Decoration *decor = mark.decor_full;
+ if ((ns_id == 0 || ns_id == mark.ns)
+ && decor && kv_size(decor->virt_text)) {
+ return decor;
}
next_mark:
marktree_itr_next(buf->b_marktree, itr);
@@ -163,7 +143,20 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state)
}
}
kv_size(state->active) = 0;
- return map_size(buf->b_extmark_index);
+ return buf->b_marktree->n_keys;
+}
+
+Decoration get_decor(mtkey_t mark)
+{
+ if (mark.decor_full) {
+ return *mark.decor_full;
+ } else {
+ Decoration fake = DECORATION_INIT;
+ fake.hl_id = mark.hl_id;
+ fake.priority = mark.priority;
+ fake.hl_eol = (mark.flags & MT_FLAG_HL_EOL);
+ return fake;
+ }
}
@@ -176,42 +169,35 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
}
marktree_itr_rewind(buf->b_marktree, state->itr);
while (true) {
- mtmark_t mark = marktree_itr_current(state->itr);
- if (mark.row < 0) { // || mark.row > end_row
+ mtkey_t mark = marktree_itr_current(state->itr);
+ if (mark.pos.row < 0) { // || mark.row > end_row
break;
}
- if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG)
- || marktree_decor_level(mark.id) < kDecorLevelVisible) {
+ if ((mark.pos.row < top_row && mt_end(mark))
+ || marktree_decor_level(mark) < kDecorLevelVisible) {
goto next_mark;
}
- uint64_t start_id = mark.id & ~MARKTREE_END_FLAG;
- ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index,
- start_id, false);
- if (!item || !item->decor) {
- goto next_mark;
- }
- Decoration *decor = item->decor;
+ Decoration decor = get_decor(mark);
- mtpos_t altpos = marktree_lookup(buf->b_marktree,
- mark.id^MARKTREE_END_FLAG, NULL);
+ mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
- if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row
- && !kv_size(decor->virt_text))
- || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) {
+ if ((!mt_end(mark) && altpos.row < top_row
+ && !kv_size(decor.virt_text))
+ || (mt_end(mark) && altpos.row >= top_row)) {
goto next_mark;
}
- if (mark.id&MARKTREE_END_FLAG) {
- decor_add(state, altpos.row, altpos.col, mark.row, mark.col,
- decor, false);
+ if (mt_end(mark)) {
+ decor_add(state, altpos.row, altpos.col, mark.pos.row, mark.pos.col,
+ &decor, false);
} else {
if (altpos.row == -1) {
- altpos.row = mark.row;
- altpos.col = mark.col;
+ altpos.row = mark.pos.row;
+ altpos.col = mark.pos.col;
}
- decor_add(state, mark.row, mark.col, altpos.row, altpos.col,
- decor, false);
+ decor_add(state, mark.pos.row, mark.pos.col, altpos.row, altpos.col,
+ &decor, false);
}
next_mark:
@@ -266,43 +252,36 @@ int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *
while (true) {
// TODO(bfredl): check duplicate entry in "intersection"
// branch
- mtmark_t mark = marktree_itr_current(state->itr);
- if (mark.row < 0 || mark.row > state->row) {
+ mtkey_t mark = marktree_itr_current(state->itr);
+ if (mark.pos.row < 0 || mark.pos.row > state->row) {
break;
- } else if (mark.row == state->row && mark.col > col) {
- state->col_until = mark.col-1;
+ } else if (mark.pos.row == state->row && mark.pos.col > col) {
+ state->col_until = mark.pos.col-1;
break;
}
- if ((mark.id&MARKTREE_END_FLAG)
- || marktree_decor_level(mark.id) < kDecorLevelVisible) {
+ if (mt_end(mark)
+ || marktree_decor_level(mark) < kDecorLevelVisible) {
goto next_mark;
}
- ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index,
- mark.id, false);
- if (!item || !item->decor) {
- goto next_mark;
- }
- Decoration *decor = item->decor;
+ Decoration decor = get_decor(mark);
- mtpos_t endpos = marktree_lookup(buf->b_marktree,
- mark.id|MARKTREE_END_FLAG, NULL);
+ mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
if (endpos.row == -1) {
- endpos.row = mark.row;
- endpos.col = mark.col;
+ endpos = mark.pos;
}
- if (endpos.row < mark.row
- || (endpos.row == mark.row && endpos.col <= mark.col)) {
- if (!kv_size(decor->virt_text)) {
+ if (endpos.row < mark.pos.row
+ || (endpos.row == mark.pos.row && endpos.col <= mark.pos.col)) {
+ if (!kv_size(decor.virt_text)) {
goto next_mark;
}
}
- decor_add(state, mark.row, mark.col, endpos.row, endpos.col,
- decor, false);
+ decor_add(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col,
+ &decor, false);
next_mark:
marktree_itr_next(buf->b_marktree, state->itr);
@@ -452,18 +431,18 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines)
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, row, 0, itr);
while (true) {
- mtmark_t mark = marktree_itr_current(itr);
- if (mark.row < 0 || mark.row >= end_row) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0 || mark.pos.row >= end_row) {
break;
- } else if (marktree_decor_level(mark.id) < kDecorLevelVirtLine) {
+ } else if (marktree_decor_level(mark) < kDecorLevelVirtLine) {
goto next_mark;
}
- bool above = mark.row > (int)(lnum - 2);
- ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false);
- if (item && item->decor && item->decor->virt_lines_above == above) {
- virt_lines += (int)kv_size(item->decor->virt_lines);
+ bool above = mark.pos.row > (int)(lnum - 2);
+ Decoration *decor = mark.decor_full;
+ if (decor && decor->virt_lines_above == above) {
+ virt_lines += (int)kv_size(decor->virt_lines);
if (lines) {
- kv_splice(*lines, item->decor->virt_lines);
+ kv_splice(*lines, decor->virt_lines);
}
}
next_mark:
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 611b4223da..02472d09e4 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -17,6 +17,8 @@ typedef enum {
kVTRightAlign,
} VirtTextPos;
+EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align" });
+
typedef enum {
kHlModeUnknown,
kHlModeReplace,
@@ -24,6 +26,8 @@ typedef enum {
kHlModeBlend,
} HlMode;
+EXTERN const char *const hl_mode_str[] INIT(= { "", "replace", "combine", "blend" });
+
typedef kvec_t(VirtTextChunk) VirtText;
#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE)
@@ -42,7 +46,6 @@ struct Decoration {
// TODO(bfredl): at some point turn this into FLAGS
bool virt_text_hide;
bool hl_eol;
- bool shared; // shared decoration, don't free
bool virt_lines_above;
// TODO(bfredl): style, signs, etc
DecorPriority priority;
@@ -50,7 +53,7 @@ struct Decoration {
int virt_text_width; // width of virt_text
};
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \
- false, false, false, false, DECOR_PRIORITY_BASE, 0, 0 }
+ false, false, false, DECOR_PRIORITY_BASE, 0, 0 }
typedef struct {
int start_row;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 340fec230c..233753839b 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -790,9 +790,14 @@ static int diff_write(buf_T *buf, diffin_T *din)
// Always use 'fileformat' set to "unix".
char_u *save_ff = buf->b_p_ff;
buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
+ const bool save_lockmarks = cmdmod.lockmarks;
+ // Writing the buffer is an implementation detail of performing the diff,
+ // so it shouldn't update the '[ and '] marks.
+ cmdmod.lockmarks = true;
int r = buf_write(buf, din->din_fname, NULL,
(linenr_T)1, buf->b_ml.ml_line_count,
NULL, false, false, false, true);
+ cmdmod.lockmarks = save_lockmarks;
free_string_option(buf->b_p_ff);
buf->b_p_ff = save_ff;
return r;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 5eef4350b7..095fa14752 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -260,7 +260,7 @@ static colnr_T Insstart_blank_vcol; // vcol for first inserted blank
static bool update_Insstart_orig = true; // set Insstart_orig to Insstart
static char_u *last_insert = NULL; // the text of the previous insert,
- // K_SPECIAL and CSI are escaped
+ // K_SPECIAL is escaped
static int last_insert_skip; // nr of chars in front of previous insert
static int new_insert_skip; // nr of chars in front of current insert
static int did_restart_edit; // "restart_edit" when calling edit()
@@ -663,8 +663,12 @@ static int insert_execute(VimState *state, int key)
InsertState *const s = (InsertState *)state;
if (stop_insert_mode) {
// Insert mode ended, possibly from a callback.
+ if (key != K_IGNORE && key != K_NOP) {
+ vungetc(key);
+ }
s->count = 0;
s->nomove = true;
+ ins_compl_prep(ESC);
return 0;
}
@@ -909,7 +913,7 @@ static int insert_handle_key(InsertState *s)
ins_ctrl_o();
// don't move the cursor left when 'virtualedit' has "onemore".
- if (ve_flags & VE_ONEMORE) {
+ if (get_ve_flags() & VE_ONEMORE) {
ins_at_eol = false;
s->nomove = true;
}
@@ -1082,11 +1086,15 @@ static int insert_handle_key(InsertState *s)
map_execute_lua();
check_pum:
+ // nvim_select_popupmenu_item() can be called from the handling of
+ // K_EVENT, K_COMMAND, or K_LUA.
// TODO(bfredl): Not entirely sure this indirection is necessary
// but doing like this ensures using nvim_select_popupmenu_item is
// equivalent to selecting the item with a typed key.
if (pum_want.active) {
if (pum_visible()) {
+ // Set this to NULL so that ins_complete() will update the message.
+ edit_submode_extra = NULL;
insert_do_complete(s);
if (pum_want.finish) {
// accept the item and stop completion
@@ -3620,7 +3628,7 @@ static bool ins_compl_prep(int c)
// Ignore end of Select mode mapping and mouse scroll buttons.
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
|| c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT
- || c == K_COMMAND) {
+ || c == K_COMMAND || c == K_LUA) {
return retval;
}
@@ -4986,7 +4994,7 @@ void ins_compl_check_keys(int frequency, int in_compl_func)
*/
static int ins_compl_key2dir(int c)
{
- if (c == K_EVENT || c == K_COMMAND) {
+ if (c == K_EVENT || c == K_COMMAND || c == K_LUA) {
return pum_want.item < pum_selected_item ? BACKWARD : FORWARD;
}
if (c == Ctrl_P || c == Ctrl_L
@@ -5016,7 +5024,7 @@ static int ins_compl_key2count(int c)
{
int h;
- if (c == K_EVENT || c == K_COMMAND) {
+ if (c == K_EVENT || c == K_COMMAND || c == K_LUA) {
int offset = pum_want.item - pum_selected_item;
return abs(offset);
}
@@ -5050,6 +5058,7 @@ static bool ins_compl_use_match(int c)
return false;
case K_EVENT:
case K_COMMAND:
+ case K_LUA:
return pum_want.active && pum_want.insert;
}
return true;
@@ -5628,8 +5637,12 @@ int get_literal(void)
i = 0;
for (;;) {
nc = plain_vgetc();
- if (!(State & CMDLINE)
- && MB_BYTE2LEN_CHECK(nc) == 1) {
+ if ((mod_mask & ~MOD_MASK_SHIFT) != 0) {
+ // A character with non-Shift modifiers should not be a valid
+ // character for i_CTRL-V_digit.
+ break;
+ }
+ if (!(State & CMDLINE) && MB_BYTE2LEN_CHECK(nc) == 1) {
add_to_showcmd(nc);
}
if (nc == 'x' || nc == 'X') {
@@ -5695,6 +5708,8 @@ int get_literal(void)
--no_mapping;
if (nc) {
vungetc(nc);
+ // A character typed with i_CTRL-V_digit cannot have modifiers.
+ mod_mask = 0;
}
got_int = false; // CTRL-C typed after CTRL-V is not an interrupt
return cc;
@@ -6814,7 +6829,7 @@ void free_last_insert(void)
/// Add character "c" to buffer "s"
///
-/// Escapes the special meaning of K_SPECIAL and CSI, handles multi-byte
+/// Escapes the special meaning of K_SPECIAL, handles multi-byte
/// characters.
///
/// @param[in] c Character to add.
@@ -6828,7 +6843,7 @@ char_u *add_char2buf(int c, char_u *s)
const int len = utf_char2bytes(c, temp);
for (int i = 0; i < len; i++) {
c = temp[i];
- // Need to escape K_SPECIAL and CSI like in the typeahead buffer.
+ // Need to escape K_SPECIAL like in the typeahead buffer.
if (c == K_SPECIAL) {
*s++ = K_SPECIAL;
*s++ = KS_SPECIAL;
@@ -6902,8 +6917,7 @@ int oneright(void)
// move "l" bytes right, but don't end up on the NUL, unless 'virtualedit'
// contains "onemore".
- if (ptr[l] == NUL
- && (ve_flags & VE_ONEMORE) == 0) {
+ if (ptr[l] == NUL && (get_ve_flags() & VE_ONEMORE) == 0) {
return FAIL;
}
curwin->w_cursor.col += l;
@@ -8025,7 +8039,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
&& !VIsual_active
))
&& !revins_on) {
- if (curwin->w_cursor.coladd > 0 || ve_flags == VE_ALL) {
+ if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL) {
oneleft();
if (restart_edit != NUL) {
curwin->w_cursor.coladd++;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 6dbdc09c3b..d25903c12a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6468,6 +6468,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (argvars[0].v_type == VAR_DICT) {
vimvars[VV_KEY].vv_type = VAR_STRING;
+ const VarLockStatus prev_lock = d->dv_lock;
+ if (map && d->dv_lock == VAR_UNLOCKED) {
+ d->dv_lock = VAR_LOCKED;
+ }
ht = &d->dv_hashtab;
hash_lock(ht);
todo = (int)ht->ht_used;
@@ -6498,6 +6502,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
}
hash_unlock(ht);
+ d->dv_lock = prev_lock;
} else if (argvars[0].v_type == VAR_BLOB) {
vimvars[VV_KEY].vv_type = VAR_NUMBER;
@@ -6530,6 +6535,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
assert(argvars[0].v_type == VAR_LIST);
vimvars[VV_KEY].vv_type = VAR_NUMBER;
+ const VarLockStatus prev_lock = tv_list_locked(l);
+ if (map && tv_list_locked(l) == VAR_UNLOCKED) {
+ tv_list_set_lock(l, VAR_LOCKED);
+ }
for (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map
&& var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
@@ -6548,6 +6557,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
idx++;
}
+ tv_list_set_lock(l, prev_lock);
}
restore_vimvar(VV_KEY, &save_key);
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7701688b49..4a07f6a850 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -10215,6 +10215,10 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) {
return;
}
+ if (!tv_dict_get_callback(opts, S_LEN("on_print"), &on_print)) {
+ return;
+ }
+
on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered");
if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) {
on_stdin.self = opts;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index d1275d6512..ad01c01499 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -81,7 +81,8 @@ typedef struct {
} data;
CallbackType type;
} Callback;
-#define CALLBACK_NONE ((Callback){ .type = kCallbackNone })
+#define CALLBACK_INIT { .type = kCallbackNone }
+#define CALLBACK_NONE ((Callback)CALLBACK_INIT)
/// Structure holding dictionary watcher
typedef struct dict_watcher {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 95390b1a70..e6d63d08a7 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -980,8 +980,10 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
foldMoveRange(win, &win->w_folds, line1, line2, dest);
}
}
- curbuf->b_op_start.lnum = dest - num_lines + 1;
- curbuf->b_op_end.lnum = dest;
+ if (!cmdmod.lockmarks) {
+ curbuf->b_op_start.lnum = dest - num_lines + 1;
+ curbuf->b_op_end.lnum = dest;
+ }
line_off = -num_lines;
byte_off = -extent_byte;
} else {
@@ -991,10 +993,14 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
foldMoveRange(win, &win->w_folds, dest + 1, line1 - 1, line2);
}
}
- curbuf->b_op_start.lnum = dest + 1;
- curbuf->b_op_end.lnum = dest + num_lines;
+ if (!cmdmod.lockmarks) {
+ curbuf->b_op_start.lnum = dest + 1;
+ curbuf->b_op_end.lnum = dest + num_lines;
+ }
+ }
+ if (!cmdmod.lockmarks) {
+ curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
}
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
mark_adjust_nofold(last_line - num_lines + 1, last_line,
-(last_line - dest - extra), 0L, kExtmarkNOOP);
@@ -1057,9 +1063,11 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
char_u *p;
count = line2 - line1 + 1;
- curbuf->b_op_start.lnum = n + 1;
- curbuf->b_op_end.lnum = n + count;
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
+ if (!cmdmod.lockmarks) {
+ curbuf->b_op_start.lnum = n + 1;
+ curbuf->b_op_end.lnum = n + count;
+ curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
+ }
/*
* there are three situations:
@@ -1099,6 +1107,9 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
}
appended_lines_mark(n, count);
+ if (VIsual_active) {
+ check_pos(curbuf, &VIsual);
+ }
msgmore((long)count);
}
@@ -1269,12 +1280,18 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd,
char_u *cmd_buf;
buf_T *old_curbuf = curbuf;
int shell_flags = 0;
+ const pos_T orig_start = curbuf->b_op_start;
+ const pos_T orig_end = curbuf->b_op_end;
const int stmp = p_stmp;
if (*cmd == NUL) { // no filter command
return;
}
+ const bool save_lockmarks = cmdmod.lockmarks;
+ // Temporarily disable lockmarks since that's needed to propagate changed
+ // regions of the buffer for foldUpdate(), linecount, etc.
+ cmdmod.lockmarks = false;
cursor_save = curwin->w_cursor;
linecount = line2 - line1 + 1;
@@ -1455,10 +1472,15 @@ error:
filterend:
+ cmdmod.lockmarks = save_lockmarks;
if (curbuf != old_curbuf) {
no_wait_return--;
emsg(_("E135: *Filter* Autocommands must not change current buffer"));
+ } else if (cmdmod.lockmarks) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
}
+
if (itmp != NULL) {
os_remove((char *)itmp);
}
@@ -3031,14 +3053,15 @@ void ex_append(exarg_T *eap)
// eap->line2 pointed to the end of the buffer and nothing was appended)
// "end" is set to lnum when something has been appended, otherwise
// it is the same as "start" -- Acevedo
- curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ?
- eap->line2 + 1 : curbuf->b_ml.ml_line_count;
- if (eap->cmdidx != CMD_append) {
- --curbuf->b_op_start.lnum;
+ if (!cmdmod.lockmarks) {
+ curbuf->b_op_start.lnum
+ = (eap->line2 < curbuf->b_ml.ml_line_count) ? eap->line2 + 1 : curbuf->b_ml.ml_line_count;
+ if (eap->cmdidx != CMD_append) {
+ curbuf->b_op_start.lnum--;
+ }
+ curbuf->b_op_end.lnum = (eap->line2 < lnum) ? lnum : curbuf->b_op_start.lnum;
+ curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
}
- curbuf->b_op_end.lnum = (eap->line2 < lnum)
- ? lnum : curbuf->b_op_start.lnum;
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
curwin->w_cursor.lnum = lnum;
check_cursor_lnum();
beginline(BL_SOL | BL_FIX);
@@ -4351,10 +4374,12 @@ skip:
}
if (sub_nsubs > start_nsubs) {
- // Set the '[ and '] marks.
- curbuf->b_op_start.lnum = eap->line1;
- curbuf->b_op_end.lnum = line2;
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
+ if (!cmdmod.lockmarks) {
+ // Set the '[ and '] marks.
+ curbuf->b_op_start.lnum = eap->line1;
+ curbuf->b_op_end.lnum = line2;
+ curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
+ }
if (!global_busy) {
// when interactive leave cursor on the match
@@ -5068,8 +5093,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
&& ((arg[1] != NUL && arg[2] == NUL)
|| (vim_strchr((char_u *)"%_z@", arg[1]) != NULL
&& arg[2] != NUL))) {
- STRCPY(d, "/\\\\");
- STRCPY(d + 3, arg + 1);
+ vim_snprintf((char *)d, IOSIZE, "/\\\\%s", arg + 1);
// Check for "/\\_$", should be "/\\_\$"
if (d[3] == '_' && d[4] == '$') {
STRCPY(d + 4, "\\$");
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index c388373ac1..c391cf96aa 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -2413,7 +2413,7 @@ module.cmds = {
},
{
command='set',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
@@ -2425,13 +2425,13 @@ module.cmds = {
},
{
command='setglobal',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
{
command='setlocal',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 2e8d39ec30..846789233f 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1613,7 +1613,7 @@ void ex_compiler(exarg_T *eap)
if (old_cur_comp != NULL) {
old_cur_comp = vim_strsave(old_cur_comp);
}
- do_cmdline_cmd("command -nargs=* CompilerSet setlocal <args>");
+ do_cmdline_cmd("command -nargs=* -keepscript CompilerSet setlocal <args>");
}
do_unlet(S_LEN("g:current_compiler"), true);
do_unlet(S_LEN("b:current_compiler"), true);
@@ -2323,9 +2323,11 @@ void ex_scriptnames(exarg_T *eap)
for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
if (SCRIPT_ITEM(i).sn_name != NULL) {
- home_replace(NULL, SCRIPT_ITEM(i).sn_name,
- NameBuff, MAXPATHL, true);
- smsg("%3d: %s", i, NameBuff);
+ home_replace(NULL, SCRIPT_ITEM(i).sn_name, NameBuff, MAXPATHL, true);
+ vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff);
+ msg_putchar('\n');
+ msg_outtrans(IObuff);
+ line_breakcheck();
}
}
}
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index e5eab61f9e..eaf5f627b6 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -61,6 +61,7 @@
// current buffer is locked
#define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer
#define EX_FLAGS 0x200000 // allow flags after count in argument
+#define EX_KEEPSCRIPT 0x4000000 // keep sctx of where command was invoked
#define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed
#define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file
#define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 71c34f98ff..c30d58a8eb 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2891,13 +2891,17 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
exarg_T ea;
char_u *name = argvars[0].vval.v_string;
- while (name[0] != NUL && name[0] == ':') {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (name == NULL) {
+ return;
+ }
+
+ while (*name == ':') {
name++;
}
name = skip_range(name, NULL);
- rettv->v_type = VAR_STRING;
-
ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
ea.cmdidx = (cmdidx_T)0;
char_u *p = find_command(&ea, NULL);
@@ -2906,7 +2910,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
- ? get_user_commands(NULL, ea.useridx)
+ ? get_user_command_name(ea.useridx, ea.cmdidx)
: cmdnames[ea.cmdidx].cmd_name);
}
@@ -4370,7 +4374,7 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
++i;
}
len = (int)STRLEN(p);
- new_cmdline = xmalloc(STRLEN(program) + i * (len - 2) + 1);
+ new_cmdline = xmalloc(STRLEN(program) + (size_t)i * (len - 2) + 1);
ptr = new_cmdline;
while ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) {
i = (int)(pos - program);
@@ -5151,7 +5155,7 @@ static int check_more(int message, bool forceit)
char_u *get_command_name(expand_T *xp, int idx)
{
if (idx >= CMD_SIZE) {
- return get_user_command_name(idx);
+ return expand_user_command_name(idx);
}
return cmdnames[idx].cmd_name;
}
@@ -5517,6 +5521,8 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int
*flags |= UC_BUFFER;
} else if (STRNICMP(attr, "register", len) == 0) {
*argt |= EX_REGSTR;
+ } else if (STRNICMP(attr, "keepscript", len) == 0) {
+ *argt |= EX_KEEPSCRIPT;
} else if (STRNICMP(attr, "bar", len) == 0) {
*argt |= EX_TRLBAR;
} else {
@@ -6028,7 +6034,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
break;
}
- case ct_MODS: {
+ case ct_MODS:
result = quote ? 2 : 0;
if (buf != NULL) {
if (quote) {
@@ -6044,7 +6050,6 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
*buf = '"';
}
break;
- }
case ct_REGISTER:
result = eap->regname ? 1 : 0;
@@ -6205,7 +6210,6 @@ static void do_ucmd(exarg_T *eap)
// K_SPECIAL has been put in the buffer as K_SPECIAL
// KS_SPECIAL KE_FILLER, like for mappings, but
// do_cmdline() doesn't handle that, so convert it back.
- // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI.
len = ksp - p;
if (len > 0) {
memmove(q, p, len);
@@ -6258,15 +6262,19 @@ static void do_ucmd(exarg_T *eap)
buf = xmalloc(totlen + 1);
}
- current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
+ if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) {
+ current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
+ }
(void)do_cmdline(buf, eap->getline, eap->cookie,
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
- current_sctx = save_current_sctx;
+ if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) {
+ current_sctx = save_current_sctx;
+ }
xfree(buf);
xfree(split_buf);
}
-static char_u *get_user_command_name(int idx)
+static char_u *expand_user_command_name(int idx)
{
return get_user_commands(NULL, idx - CMD_SIZE);
}
@@ -6299,6 +6307,24 @@ char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
return NULL;
}
+// Get the name of user command "idx". "cmdidx" can be CMD_USER or
+// CMD_USER_BUF.
+// Returns NULL if the command is not found.
+static char_u *get_user_command_name(int idx, int cmdidx)
+{
+ if (cmdidx == CMD_USER && idx < ucmds.ga_len) {
+ return USER_CMD(idx)->uc_name;
+ }
+ if (cmdidx == CMD_USER_BUF) {
+ // In cmdwin, the alternative buffer should be used.
+ buf_T *buf = (cmdwin_type != 0 && get_cmdline_type() == NUL) ? prevwin->w_buffer : curbuf;
+ if (idx < buf->b_ucmds.ga_len) {
+ return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
+ }
+ }
+ return NULL;
+}
+
/*
* Function given to ExpandGeneric() to obtain the list of user command
* attributes.
@@ -6307,7 +6333,7 @@ char_u *get_user_cmd_flags(expand_T *xp, int idx)
{
static char *user_cmd_flags[] = { "addr", "bang", "bar",
"buffer", "complete", "count",
- "nargs", "range", "register" };
+ "nargs", "range", "register", "keepscript" };
if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) {
return NULL;
@@ -8628,7 +8654,7 @@ static void ex_normal(exarg_T *eap)
return;
}
- // vgetc() expects a CSI and K_SPECIAL to have been escaped. Don't do
+ // vgetc() expects K_SPECIAL to have been escaped. Don't do
// this for the K_SPECIAL leading byte, otherwise special keys will not
// work.
{
@@ -8637,8 +8663,7 @@ static void ex_normal(exarg_T *eap)
// Count the number of characters to be escaped.
for (p = eap->arg; *p != NUL; p++) {
for (l = utfc_ptr2len(p) - 1; l > 0; l--) {
- if (*++p == K_SPECIAL // trailbyte K_SPECIAL or CSI
- ) {
+ if (*++p == K_SPECIAL) { // trailbyte K_SPECIAL
len += 2;
}
}
@@ -9547,16 +9572,12 @@ static void ex_filetype(exarg_T *eap)
}
}
-/// Set all :filetype options ON if user did not explicitly set any to OFF.
-void filetype_maybe_enable(void)
+/// Source ftplugin.vim and indent.vim to create the necessary FileType
+/// autocommands. We do this separately from filetype.vim so that these
+/// autocommands will always fire first (and thus can be overriden) while still
+/// allowing general filetype detection to be disabled in the user's init file.
+void filetype_plugin_enable(void)
{
- if (filetype_detect == kNone) {
- // Normally .vim files are sourced before .lua files when both are
- // supported, but we reverse the order here because we want the Lua
- // autocommand to be defined first so that it runs first
- source_runtime(FILETYPE_FILE, DIP_ALL);
- filetype_detect = kTrue;
- }
if (filetype_plugin == kNone) {
source_runtime(FTPLUGIN_FILE, DIP_ALL);
filetype_plugin = kTrue;
@@ -9567,6 +9588,18 @@ void filetype_maybe_enable(void)
}
}
+/// Enable filetype detection if the user did not explicitly disable it.
+void filetype_maybe_enable(void)
+{
+ if (filetype_detect == kNone) {
+ // Normally .vim files are sourced before .lua files when both are
+ // supported, but we reverse the order here because we want the Lua
+ // autocommand to be defined first so that it runs first
+ source_runtime(FILETYPE_FILE, DIP_ALL);
+ filetype_detect = kTrue;
+ }
+}
+
/// ":setfiletype [FALLBACK] {name}"
static void ex_setfiletype(exarg_T *eap)
{
@@ -9593,18 +9626,6 @@ static void ex_digraphs(exarg_T *eap)
}
}
-static void ex_set(exarg_T *eap)
-{
- int flags = 0;
-
- if (eap->cmdidx == CMD_setlocal) {
- flags = OPT_LOCAL;
- } else if (eap->cmdidx == CMD_setglobal) {
- flags = OPT_GLOBAL;
- }
- (void)do_set(eap->arg, flags);
-}
-
void set_no_hlsearch(bool flag)
{
no_hlsearch = flag;
@@ -9839,6 +9860,7 @@ Dictionary commands_array(buf_T *buf)
PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG)));
PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR)));
PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR)));
+ PUT(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT)));
switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) {
case 0:
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 78b8e43e65..fd75cfc7f8 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -772,7 +772,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.cmdindent = (s->firstc > 0 ? s->indent : 0);
// alloc initial ccline.cmdbuff
- alloc_cmdbuff(exmode_active ? 250 : s->indent + 1);
+ alloc_cmdbuff(indent + 50);
ccline.cmdlen = ccline.cmdpos = 0;
ccline.cmdbuff[0] = NUL;
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index c4d8f75a21..48e57e20e1 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -48,28 +48,29 @@
# include "extmark.c.generated.h"
#endif
-static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put)
+static uint32_t *buf_ns_ref(buf_T *buf, uint32_t ns_id, bool put)
{
- return map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, put);
+ return map_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, put);
}
/// Create or update an extmark
///
/// must not be used during iteration!
-/// @returns the internal mark id
-uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T col, int end_row,
- colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity,
- ExtmarkOp op)
+void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col, int end_row,
+ colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity,
+ ExtmarkOp op)
{
- ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true);
- assert(ns != NULL);
- mtpos_t old_pos;
- uint64_t mark = 0;
- uint64_t id = idp ? *idp : 0;
+ uint32_t *ns = buf_ns_ref(buf, ns_id, true);
+ uint32_t id = idp ? *idp : 0;
+ bool decor_full = false;
uint8_t decor_level = kDecorLevelNone; // no decor
if (decor) {
+ if (kv_size(decor->virt_text) || kv_size(decor->virt_lines)) {
+ decor_full = true;
+ decor = xmemdup(decor, sizeof *decor);
+ }
decor_level = kDecorLevelVisible; // decor affects redraw
if (kv_size(decor->virt_lines)) {
decor_level = kDecorLevelVirtLine; // decor affects horizontal size
@@ -77,50 +78,64 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T
}
if (id == 0) {
- id = ns->free_id++;
+ id = ++*ns;
} else {
- uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id);
- if (old_mark) {
- if (old_mark & MARKTREE_PAIRED_FLAG || end_row > -1) {
+ MarkTreeIter itr[1] = { 0 };
+ mtkey_t old_mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
+ if (old_mark.id) {
+ if (mt_paired(old_mark) || end_row > -1) {
extmark_del(buf, ns_id, id);
} else {
- MarkTreeIter itr[1] = { 0 };
- old_pos = marktree_lookup(buf->b_marktree, old_mark, itr);
+ // TODO(bfredl): we need to do more if "revising" a decoration mark.
assert(itr->node);
- if (old_pos.row == row && old_pos.col == col) {
- ExtmarkItem it = map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index,
- old_mark);
- if (it.decor) {
- decor_remove(buf, row, row, it.decor);
+ if (old_mark.pos.row == row && old_mark.pos.col == col) {
+ if (marktree_decor_level(old_mark) > kDecorLevelNone) {
+ decor_remove(buf, row, row, old_mark.decor_full);
+ old_mark.decor_full = NULL;
+ }
+ old_mark.flags = 0;
+ if (decor_full) {
+ old_mark.decor_full = decor;
+ } else if (decor) {
+ old_mark.hl_id = decor->hl_id;
+ // Workaround: the gcc compiler of functionaltest-lua build
+ // apparently incapable of handling basic integer constants.
+ // This can be underanged as soon as we bump minimal gcc version.
+ old_mark.flags = (uint16_t)(old_mark.flags
+ | (decor->hl_eol ? (uint16_t)MT_FLAG_HL_EOL : (uint16_t)0));
+ old_mark.priority = decor->priority;
}
- mark = marktree_revise(buf->b_marktree, itr, decor_level);
+ marktree_revise(buf->b_marktree, itr, decor_level, old_mark);
goto revised;
}
marktree_del_itr(buf->b_marktree, itr, false);
}
} else {
- ns->free_id = MAX(ns->free_id, id+1);
+ *ns = MAX(*ns, id);
}
}
- if (end_row > -1) {
- mark = marktree_put_pair(buf->b_marktree,
- row, col, right_gravity,
- end_row, end_col, end_right_gravity, decor_level);
- } else {
- mark = marktree_put(buf->b_marktree, row, col, right_gravity, decor_level);
+ mtkey_t mark = { { row, col }, ns_id, id, 0,
+ mt_flags(right_gravity, decor_level), 0, NULL };
+ if (decor_full) {
+ mark.decor_full = decor;
+ } else if (decor) {
+ mark.hl_id = decor->hl_id;
+ // workaround: see above
+ mark.flags = (uint16_t)(mark.flags | (decor->hl_eol ? (uint16_t)MT_FLAG_HL_EOL : (uint16_t)0));
+ mark.priority = decor->priority;
}
-revised:
- map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark,
- (ExtmarkItem){ ns_id, id, decor });
- map_put(uint64_t, uint64_t)(ns->map, id, mark);
+ marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity);
+revised:
if (op != kExtmarkNoUndo) {
// TODO(bfredl): this doesn't cover all the cases and probably shouldn't
// be done "prematurely". Any movement in undo history might necessitate
- // adding new marks to old undo headers.
- u_extmark_set(buf, mark, row, col);
+ // adding new marks to old undo headers. add a test case for this (doesn't
+ // fail extmark_spec.lua, and it should)
+ uint64_t mark_id = mt_lookup_id(ns_id, id, false);
+ u_extmark_set(buf, mark_id, row, col);
}
if (decor) {
@@ -133,18 +148,17 @@ revised:
if (idp) {
*idp = id;
}
- return mark;
}
static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col)
{
MarkTreeIter itr[1] = { 0 };
- mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr);
- if (pos.row == -1) {
+ mtkey_t key = marktree_lookup(buf->b_marktree, mark, itr);
+ if (key.pos.row == -1) {
return false;
}
- if (pos.row == row && pos.col == col) {
+ if (key.pos.row == row && key.pos.col == col) {
return true;
}
@@ -154,45 +168,35 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col)
// Remove an extmark
// Returns 0 on missing id
-bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id)
+bool extmark_del(buf_T *buf, uint32_t ns_id, uint32_t id)
{
- ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false);
- if (!ns) {
- return false;
- }
-
- uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id);
- if (!mark) {
+ MarkTreeIter itr[1] = { 0 };
+ mtkey_t key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
+ if (!key.id) {
return false;
}
-
- MarkTreeIter itr[1] = { 0 };
- mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr);
- assert(pos.row >= 0);
+ assert(key.pos.row >= 0);
marktree_del_itr(buf->b_marktree, itr, false);
- ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark);
- mtpos_t pos2 = pos;
- if (mark & MARKTREE_PAIRED_FLAG) {
- pos2 = marktree_lookup(buf->b_marktree, mark|MARKTREE_END_FLAG, itr);
- assert(pos2.row >= 0);
+ mtkey_t key2 = key;
+
+ if (mt_paired(key)) {
+ key2 = marktree_lookup_ns(buf->b_marktree, ns_id, id, true, itr);
+ assert(key2.pos.row >= 0);
marktree_del_itr(buf->b_marktree, itr, false);
}
- if (item.decor) {
- decor_remove(buf, pos.row, pos2.row, item.decor);
+ if (marktree_decor_level(key) > kDecorLevelNone) {
+ decor_remove(buf, key.pos.row, key2.pos.row, key.decor_full);
}
- map_del(uint64_t, uint64_t)(ns->map, id);
- map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark);
-
// TODO(bfredl): delete it from current undo header, opportunistically?
return true;
}
// Free extmarks in a ns between lines
// if ns = 0, it means clear all namespaces
-bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col)
+bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col)
{
if (!map_size(buf->b_extmark_ns)) {
return false;
@@ -201,68 +205,58 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r
bool marks_cleared = false;
bool all_ns = (ns_id == 0);
- ExtmarkNs *ns = NULL;
+ uint32_t *ns = NULL;
if (!all_ns) {
ns = buf_ns_ref(buf, ns_id, false);
if (!ns) {
// nothing to do
return false;
}
-
- // TODO(bfredl): if map_size(ns->map) << buf->b_marktree.n_nodes
- // it could be faster to iterate over the map instead
}
// the value is either zero or the lnum (row+1) if highlight was present.
static Map(uint64_t, ssize_t) delete_set = MAP_INIT;
- typedef struct { Decoration *decor; int row1; } DecorItem;
+ typedef struct { int row1; } DecorItem;
static kvec_t(DecorItem) decors;
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, l_row, l_col, itr);
while (true) {
- mtmark_t mark = marktree_itr_current(itr);
- if (mark.row < 0
- || mark.row > u_row
- || (mark.row == u_row && mark.col > u_col)) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0
+ || mark.pos.row > u_row
+ || (mark.pos.row == u_row && mark.pos.col > u_col)) {
break;
}
- ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mark.id,
+ ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark),
false);
if (del_status) {
marktree_del_itr(buf->b_marktree, itr, false);
if (*del_status >= 0) { // we had a decor_id
DecorItem it = kv_A(decors, *del_status);
- decor_remove(buf, it.row1, mark.row, it.decor);
+ decor_remove(buf, it.row1, mark.pos.row, mark.decor_full);
}
- map_del(uint64_t, ssize_t)(&delete_set, mark.id);
+ map_del(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark));
continue;
}
- uint64_t start_id = mark.id & ~MARKTREE_END_FLAG;
- ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index,
- start_id);
-
- assert(item.ns_id > 0 && item.mark_id > 0);
- if (item.mark_id > 0 && (item.ns_id == ns_id || all_ns)) {
+ assert(mark.ns > 0 && mark.id > 0);
+ if (mark.ns == ns_id || all_ns) {
marks_cleared = true;
- if (mark.id & MARKTREE_PAIRED_FLAG) {
- uint64_t other = mark.id ^ MARKTREE_END_FLAG;
+ if (mt_paired(mark)) {
+ uint64_t other = mt_lookup_id(mark.ns, mark.id, !mt_end(mark));
ssize_t decor_id = -1;
- if (item.decor) {
+ if (marktree_decor_level(mark) > kDecorLevelNone) {
// Save the decoration and the first pos. Clear the decoration
// later when we know the full range.
decor_id = (ssize_t)kv_size(decors);
kv_push(decors,
- ((DecorItem) { .decor = item.decor, .row1 = mark.row }));
+ ((DecorItem) { .row1 = mark.pos.row }));
}
map_put(uint64_t, ssize_t)(&delete_set, other, decor_id);
- } else if (item.decor) {
- decor_remove(buf, mark.row, mark.row, item.decor);
+ } else if (mark.decor_full) {
+ decor_remove(buf, mark.pos.row, mark.pos.row, mark.decor_full);
}
- ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns;
- map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id);
- map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id);
marktree_del_itr(buf->b_marktree, itr, false);
} else {
marktree_itr_next(buf->b_marktree, itr);
@@ -271,12 +265,12 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r
uint64_t id;
ssize_t decor_id;
map_foreach(&delete_set, id, decor_id, {
- mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr);
+ mtkey_t mark = marktree_lookup(buf->b_marktree, id, itr);
assert(itr->node);
marktree_del_itr(buf->b_marktree, itr, false);
if (decor_id >= 0) {
DecorItem it = kv_A(decors, decor_id);
- decor_remove(buf, it.row1, pos.row, it.decor);
+ decor_remove(buf, it.row1, mark.pos.row, mark.decor_full);
}
});
map_clear(uint64_t, ssize_t)(&delete_set);
@@ -290,7 +284,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r
// will be searched to the start, or end
// dir can be set to control the order of the array
// amount = amount of marks to find or -1 for all
-ExtmarkInfoArray extmark_get(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_row,
+ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row,
colnr_T u_col, int64_t amount, bool reverse)
{
ExtmarkInfoArray array = KV_INITIAL_VALUE;
@@ -300,30 +294,26 @@ ExtmarkInfoArray extmark_get(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_co
itr, reverse, false, NULL);
int order = reverse ? -1 : 1;
while ((int64_t)kv_size(array) < amount) {
- mtmark_t mark = marktree_itr_current(itr);
- mtpos_t endpos = { -1, -1 };
- if (mark.row < 0
- || (mark.row - u_row) * order > 0
- || (mark.row == u_row && (mark.col - u_col) * order > 0)) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0
+ || (mark.pos.row - u_row) * order > 0
+ || (mark.pos.row == u_row && (mark.pos.col - u_col) * order > 0)) {
break;
}
- if (mark.id & MARKTREE_END_FLAG) {
+ if (mt_end(mark)) {
goto next_mark;
- } else if (mark.id & MARKTREE_PAIRED_FLAG) {
- endpos = marktree_lookup(buf->b_marktree, mark.id | MARKTREE_END_FLAG,
- NULL);
}
-
- ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index,
- mark.id);
- if (item.ns_id == ns_id) {
- kv_push(array, ((ExtmarkInfo) { .ns_id = item.ns_id,
- .mark_id = item.mark_id,
- .row = mark.row, .col = mark.col,
- .end_row = endpos.row,
- .end_col = endpos.col,
- .decor = item.decor }));
+ if (mark.ns == ns_id) {
+ mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL);
+ kv_push(array, ((ExtmarkInfo) { .ns_id = mark.ns,
+ .mark_id = mark.id,
+ .row = mark.pos.row, .col = mark.pos.col,
+ .end_row = end.pos.row,
+ .end_col = end.pos.col,
+ .right_gravity = mt_right(mark),
+ .end_right_gravity = mt_right(end),
+ .decor = get_decor(mark) }));
}
next_mark:
if (reverse) {
@@ -336,36 +326,25 @@ next_mark:
}
// Lookup an extmark by id
-ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id)
+ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id)
{
- ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false);
- ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, NULL };
- if (!ns) {
+ ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, false, false, DECORATION_INIT };
+ mtkey_t mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL);
+ if (!mark.id) {
return ret;
}
-
- uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id);
- if (!mark) {
- return ret;
- }
-
- mtpos_t pos = marktree_lookup(buf->b_marktree, mark, NULL);
- mtpos_t endpos = { -1, -1 };
- if (mark & MARKTREE_PAIRED_FLAG) {
- endpos = marktree_lookup(buf->b_marktree, mark | MARKTREE_END_FLAG, NULL);
- }
- assert(pos.row >= 0);
-
- ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index,
- mark);
+ assert(mark.pos.row >= 0);
+ mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL);
ret.ns_id = ns_id;
ret.mark_id = id;
- ret.row = pos.row;
- ret.col = pos.col;
- ret.end_row = endpos.row;
- ret.end_col = endpos.col;
- ret.decor = item.decor;
+ ret.row = mark.pos.row;
+ ret.col = mark.pos.col;
+ ret.end_row = end.pos.row;
+ ret.end_col = end.pos.col;
+ ret.right_gravity = mt_right(mark);
+ ret.end_right_gravity = mt_right(end);
+ ret.decor = get_decor(mark);
return ret;
}
@@ -378,25 +357,26 @@ void extmark_free_all(buf_T *buf)
return;
}
- uint64_t id;
- ExtmarkNs ns;
- ExtmarkItem item;
+ MarkTreeIter itr[1] = { 0 };
+ marktree_itr_get(buf->b_marktree, 0, 0, itr);
+ while (true) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0) {
+ break;
+ }
- marktree_clear(buf->b_marktree);
+ // don't free mark.decor_full twice for a paired mark.
+ if (!(mt_paired(mark) && mt_end(mark))) {
+ decor_free(mark.decor_full);
+ }
- map_foreach(buf->b_extmark_ns, id, ns, {
- (void)id;
- map_destroy(uint64_t, uint64_t)(ns.map);
- });
- map_destroy(uint64_t, ExtmarkNs)(buf->b_extmark_ns);
- map_init(uint64_t, ExtmarkNs, buf->b_extmark_ns);
+ marktree_itr_next(buf->b_marktree, itr);
+ }
- map_foreach(buf->b_extmark_index, id, item, {
- (void)id;
- decor_free(item.decor);
- });
- map_destroy(uint64_t, ExtmarkItem)(buf->b_extmark_index);
- map_init(uint64_t, ExtmarkItem, buf->b_extmark_index);
+ marktree_clear(buf->b_marktree);
+
+ map_destroy(uint32_t, uint32_t)(buf->b_extmark_ns);
+ map_init(uint32_t, uint32_t, buf->b_extmark_ns);
}
@@ -437,16 +417,16 @@ void u_extmark_copy(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_c
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, l_row, l_col, itr);
while (true) {
- mtmark_t mark = marktree_itr_current(itr);
- if (mark.row < 0
- || mark.row > u_row
- || (mark.row == u_row && mark.col > u_col)) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0
+ || mark.pos.row > u_row
+ || (mark.pos.row == u_row && mark.pos.col > u_col)) {
break;
}
ExtmarkSavePos pos;
- pos.mark = mark.id;
- pos.old_row = mark.row;
- pos.old_col = mark.col;
+ pos.mark = mt_lookup_key(mark);
+ pos.old_row = mark.pos.row;
+ pos.old_col = mark.pos.col;
pos.row = -1;
pos.col = -1;
diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h
index c70db9f7aa..af9526cd43 100644
--- a/src/nvim/extmark.h
+++ b/src/nvim/extmark.h
@@ -3,6 +3,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/extmark_defs.h"
+#include "nvim/decoration.h"
#include "nvim/marktree.h"
#include "nvim/pos.h"
@@ -15,7 +16,9 @@ typedef struct {
colnr_T col;
int end_row;
colnr_T end_col;
- Decoration *decor;
+ bool right_gravity;
+ bool end_right_gravity;
+ Decoration decor; // TODO(bfredl): CHONKY
} ExtmarkInfo;
typedef kvec_t(ExtmarkInfo) ExtmarkInfoArray;
diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h
index bbe8504ebf..5570b5c71e 100644
--- a/src/nvim/extmark_defs.h
+++ b/src/nvim/extmark_defs.h
@@ -4,23 +4,11 @@
#include "nvim/lib/kvec.h"
#include "nvim/types.h"
-typedef struct Decoration Decoration;
-
typedef struct {
char *text;
int hl_id;
} VirtTextChunk;
-
-typedef struct {
- uint64_t ns_id;
- uint64_t mark_id;
- // TODO(bfredl): a lot of small allocations. Should probably use
- // kvec_t(Decoration) as an arena. Alternatively, store ns_id/mark_id
- // _inline_ in MarkTree and use the map only for decorations.
- Decoration *decor;
-} ExtmarkItem;
-
typedef struct undo_object ExtmarkUndoObject;
typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t;
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index b2cd5c510b..b4becb3066 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1433,7 +1433,11 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
rel_fname = NULL;
}
- if (first == TRUE) {
+ if (first == true) {
+ if (len == 0) {
+ return NULL;
+ }
+
// copy file name into NameBuff, expanding environment variables
save_char = ptr[len];
ptr[len] = NUL;
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index f8cf341836..f6d37adf89 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -242,6 +242,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
bool notconverted = false; // true if conversion wanted but it wasn't possible
char_u conv_rest[CONV_RESTLEN];
int conv_restlen = 0; // nr of bytes in conv_rest[]
+ pos_T orig_start;
buf_T *old_curbuf;
char_u *old_b_ffname;
char_u *old_b_fname;
@@ -298,14 +299,10 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
fname = sfname;
#endif
- /*
- * The BufReadCmd and FileReadCmd events intercept the reading process by
- * executing the associated commands instead.
- */
+ // The BufReadCmd and FileReadCmd events intercept the reading process by
+ // executing the associated commands instead.
if (!filtering && !read_stdin && !read_buffer) {
- pos_T pos;
-
- pos = curbuf->b_op_start;
+ orig_start = curbuf->b_op_start;
// Set '[ mark to the line above where the lines go (line 1 if zero).
curbuf->b_op_start.lnum = ((from == 0) ? 1 : from);
@@ -335,7 +332,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
return aborting() ? FAIL : OK;
}
- curbuf->b_op_start = pos;
+ curbuf->b_op_start = orig_start;
}
if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) {
@@ -576,9 +573,8 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
++no_wait_return; // don't wait for return yet
- /*
- * Set '[ mark to the line above where the lines go (line 1 if zero).
- */
+ // Set '[ mark to the line above where the lines go (line 1 if zero).
+ orig_start = curbuf->b_op_start;
curbuf->b_op_start.lnum = ((from == 0) ? 1 : from);
curbuf->b_op_start.col = 0;
@@ -618,6 +614,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
try_mac = (vim_strchr(p_ffs, 'm') != NULL);
try_dos = (vim_strchr(p_ffs, 'd') != NULL);
try_unix = (vim_strchr(p_ffs, 'x') != NULL);
+ curbuf->b_op_start = orig_start;
if (msg_scrolled == n) {
msg_scroll = m;
@@ -1888,13 +1885,13 @@ failed:
check_cursor_lnum();
beginline(BL_WHITE | BL_FIX); // on first non-blank
- /*
- * Set '[ and '] marks to the newly read lines.
- */
- curbuf->b_op_start.lnum = from + 1;
- curbuf->b_op_start.col = 0;
- curbuf->b_op_end.lnum = from + linecnt;
- curbuf->b_op_end.col = 0;
+ if (!cmdmod.lockmarks) {
+ // Set '[ and '] marks to the newly read lines.
+ curbuf->b_op_start.lnum = from + 1;
+ curbuf->b_op_start.col = 0;
+ curbuf->b_op_end.lnum = from + linecnt;
+ curbuf->b_op_end.col = 0;
+ }
}
msg_scroll = msg_save;
@@ -2252,6 +2249,8 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
int write_undo_file = FALSE;
context_sha256_T sha_ctx;
unsigned int bkc = get_bkc_value(buf);
+ const pos_T orig_start = buf->b_op_start;
+ const pos_T orig_end = buf->b_op_end;
if (fname == NULL || *fname == NUL) { // safety check
return FAIL;
@@ -2432,7 +2431,13 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline)
|| did_cmd || nofile_err
|| aborting()) {
- --no_wait_return;
+ if (buf != NULL && cmdmod.lockmarks) {
+ // restore the original '[ and '] positions
+ buf->b_op_start = orig_start;
+ buf->b_op_end = orig_end;
+ }
+
+ no_wait_return--;
msg_scroll = msg_save;
if (nofile_err) {
emsg(_("E676: No matching autocommands for acwrite buffer"));
@@ -2513,6 +2518,11 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
}
}
+ if (cmdmod.lockmarks) {
+ // restore the original '[ and '] positions
+ buf->b_op_start = orig_start;
+ buf->b_op_end = orig_end;
+ }
if (shortmess(SHM_OVER) && !exiting) {
msg_scroll = FALSE; // overwrite previous file message
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 5565e17597..55bcfa0e97 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -59,25 +59,18 @@
static int curscript = 0;
FileDescriptor *scriptin[NSCRIPT] = { NULL };
-/*
- * These buffers are used for storing:
- * - stuffed characters: A command that is translated into another command.
- * - redo characters: will redo the last change.
- * - recorded characters: for the "q" command.
- *
- * The bytes are stored like in the typeahead buffer:
- * - K_SPECIAL introduces a special key (two more bytes follow). A literal
- * K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER.
- * - CSI introduces a GUI termcap code (also when gui.in_use is FALSE,
- * otherwise switching the GUI on would make mappings invalid).
- * A literal CSI is stored as CSI KS_EXTRA KE_CSI.
- * These translations are also done on multi-byte characters!
- *
- * Escaping CSI bytes is done by the system-specific input functions, called
- * by ui_inchar().
- * Escaping K_SPECIAL is done by inchar().
- * Un-escaping is done by vgetc().
- */
+// These buffers are used for storing:
+// - stuffed characters: A command that is translated into another command.
+// - redo characters: will redo the last change.
+// - recorded characters: for the "q" command.
+//
+// The bytes are stored like in the typeahead buffer:
+// - K_SPECIAL introduces a special key (two more bytes follow). A literal
+// K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER.
+// These translations are also done on multi-byte characters!
+//
+// Escaping K_SPECIAL is done by inchar().
+// Un-escaping is done by vgetc().
#define MINIMAL_SIZE 20 // minimal size for b_str
@@ -170,10 +163,11 @@ void free_buff(buffheader_T *buf)
xfree(p);
}
buf->bh_first.b_next = NULL;
+ buf->bh_curr = NULL;
}
/// Return the contents of a buffer as a single string.
-/// K_SPECIAL and CSI in the returned string are escaped.
+/// K_SPECIAL in the returned string is escaped.
///
/// @param dozero count == zero is not an error
static char_u *get_buffcont(buffheader_T *buffer, int dozero)
@@ -202,11 +196,9 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero)
return p;
}
-/*
- * Return the contents of the record buffer as a single string
- * and clear the record buffer.
- * K_SPECIAL and CSI in the returned string are escaped.
- */
+/// Return the contents of the record buffer as a single string
+/// and clear the record buffer.
+/// K_SPECIAL in the returned string is escaped.
char_u *get_recorded(void)
{
char_u *p;
@@ -236,10 +228,8 @@ char_u *get_recorded(void)
return p;
}
-/*
- * Return the contents of the redo buffer as a single string.
- * K_SPECIAL and CSI in the returned string are escaped.
- */
+/// Return the contents of the redo buffer as a single string.
+/// K_SPECIAL in the returned string is escaped.
char_u *get_inserted(void)
{
return get_buffcont(&redobuff, FALSE);
@@ -247,7 +237,7 @@ char_u *get_inserted(void)
/// Add string after the current block of the given buffer
///
-/// K_SPECIAL and CSI should have been escaped already.
+/// K_SPECIAL should have been escaped already.
///
/// @param[out] buf Buffer to add to.
/// @param[in] s String to add.
@@ -295,9 +285,23 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle
}
}
-/*
- * Add number "n" to buffer "buf".
- */
+/// Delete "slen" bytes from the end of "buf".
+/// Only works when it was just added.
+static void delete_buff_tail(buffheader_T *buf, int slen)
+{
+ int len;
+
+ if (buf->bh_curr == NULL) {
+ return; // nothing to delete
+ }
+ len = (int)STRLEN(buf->bh_curr->b_str);
+ if (len >= slen) {
+ buf->bh_curr->b_str[len - slen] = NUL;
+ buf->bh_space += (size_t)slen;
+ }
+}
+
+/// Add number "n" to buffer "buf".
static void add_num_buff(buffheader_T *buf, long n)
{
char number[32];
@@ -305,10 +309,8 @@ static void add_num_buff(buffheader_T *buf, long n)
add_buff(buf, number, -1L);
}
-/*
- * Add character 'c' to buffer "buf".
- * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters.
- */
+/// Add character 'c' to buffer "buf".
+/// Translates special keys, NUL, K_SPECIAL and multibyte characters.
static void add_char_buff(buffheader_T *buf, int c)
{
uint8_t bytes[MB_MAXBYTES + 1];
@@ -340,12 +342,10 @@ static void add_char_buff(buffheader_T *buf, int c)
}
}
-/*
- * Get one byte from the read buffers. Use readbuf1 one first, use readbuf2
- * if that one is empty.
- * If advance == TRUE go to the next char.
- * No translation is done K_SPECIAL and CSI are escaped.
- */
+/// Get one byte from the read buffers. Use readbuf1 one first, use readbuf2
+/// if that one is empty.
+/// If advance == TRUE go to the next char.
+/// No translation is done K_SPECIAL is escaped.
static int read_readbuffers(int advance)
{
int c;
@@ -524,10 +524,8 @@ void restoreRedobuff(save_redo_T *save_redo)
old_redobuff = save_redo->sr_old_redobuff;
}
-/*
- * Append "s" to the redo buffer.
- * K_SPECIAL and CSI should already have been escaped.
- */
+/// Append "s" to the redo buffer.
+/// K_SPECIAL should already have been escaped.
void AppendToRedobuff(const char *s)
{
if (!block_redo) {
@@ -536,7 +534,7 @@ void AppendToRedobuff(const char *s)
}
/// Append to Redo buffer literally, escaping special characters with CTRL-V.
-/// K_SPECIAL and CSI are escaped as well.
+/// K_SPECIAL is escaped as well.
///
/// @param str String to append
/// @param len Length of `str` or -1 for up to the NUL.
@@ -584,10 +582,8 @@ void AppendToRedobuffLit(const char_u *str, int len)
}
}
-/*
- * Append a character to the redo buffer.
- * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters.
- */
+/// Append a character to the redo buffer.
+/// Translates special keys, NUL, K_SPECIAL and multibyte characters.
void AppendCharToRedobuff(int c)
{
if (!block_redo) {
@@ -605,17 +601,15 @@ void AppendNumberToRedobuff(long n)
}
}
-/*
- * Append string "s" to the stuff buffer.
- * CSI and K_SPECIAL must already have been escaped.
- */
+/// Append string "s" to the stuff buffer.
+/// K_SPECIAL must already have been escaped.
void stuffReadbuff(const char *s)
{
add_buff(&readbuf1, s, -1L);
}
/// Append string "s" to the redo stuff buffer.
-/// @remark CSI and K_SPECIAL must already have been escaped.
+/// @remark K_SPECIAL must already have been escaped.
void stuffRedoReadbuff(const char *s)
{
add_buff(&readbuf2, s, -1L);
@@ -626,11 +620,9 @@ void stuffReadbuffLen(const char *s, long len)
add_buff(&readbuf1, s, len);
}
-/*
- * Stuff "s" into the stuff buffer, leaving special key codes unmodified and
- * escaping other K_SPECIAL and CSI bytes.
- * Change CR, LF and ESC into a space.
- */
+/// Stuff "s" into the stuff buffer, leaving special key codes unmodified and
+/// escaping other K_SPECIAL bytes.
+/// Change CR, LF and ESC into a space.
void stuffReadbuffSpec(const char *s)
{
while (*s != NUL) {
@@ -648,10 +640,8 @@ void stuffReadbuffSpec(const char *s)
}
}
-/*
- * Append a character to the stuff buffer.
- * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters.
- */
+/// Append a character to the stuff buffer.
+/// Translates special keys, NUL, K_SPECIAL and multibyte characters.
void stuffcharReadbuff(int c)
{
add_char_buff(&readbuf1, c);
@@ -665,12 +655,12 @@ void stuffnumReadbuff(long n)
add_num_buff(&readbuf1, n);
}
-// Read a character from the redo buffer. Translates K_SPECIAL, CSI and
-// multibyte characters.
-// The redo buffer is left as it is.
-// If init is true, prepare for redo, return FAIL if nothing to redo, OK
-// otherwise.
-// If old_redo is true, use old_redobuff instead of redobuff.
+/// Read a character from the redo buffer. Translates K_SPECIAL and
+/// multibyte characters.
+/// The redo buffer is left as it is.
+/// If init is true, prepare for redo, return FAIL if nothing to redo, OK
+/// otherwise.
+/// If old_redo is true, use old_redobuff instead of redobuff.
static int read_redo(bool init, bool old_redo)
{
static buffblock_T *bp;
@@ -724,9 +714,9 @@ static int read_redo(bool init, bool old_redo)
return c;
}
-// Copy the rest of the redo buffer into the stuff buffer (in a slow way).
-// If old_redo is true, use old_redobuff instead of redobuff.
-// The escaped K_SPECIAL and CSI are copied without translation.
+/// Copy the rest of the redo buffer into the stuff buffer (in a slow way).
+/// If old_redo is true, use old_redobuff instead of redobuff.
+/// The escaped K_SPECIAL is copied without translation.
static void copy_redo(bool old_redo)
{
int c;
@@ -992,36 +982,45 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent
return OK;
}
-/*
- * Put character "c" back into the typeahead buffer.
- * Can be used for a character obtained by vgetc() that needs to be put back.
- * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to
- * the char.
- */
-void ins_char_typebuf(int c)
+/// Put character "c" back into the typeahead buffer.
+/// Can be used for a character obtained by vgetc() that needs to be put back.
+/// Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to
+/// the char.
+/// @return the length of what was inserted
+int ins_char_typebuf(int c, int modifier)
{
- char_u buf[MB_MAXBYTES + 1];
- if (IS_SPECIAL(c)) {
+ char_u buf[MB_MAXBYTES * 3 + 4];
+ int len = 0;
+ if (modifier != 0) {
buf[0] = K_SPECIAL;
- buf[1] = (char_u)K_SECOND(c);
- buf[2] = (char_u)K_THIRD(c);
+ buf[1] = KS_MODIFIER;
+ buf[2] = (char_u)modifier;
buf[3] = NUL;
+ len = 3;
+ }
+ if (IS_SPECIAL(c)) {
+ buf[len] = K_SPECIAL;
+ buf[len + 1] = (char_u)K_SECOND(c);
+ buf[len + 2] = (char_u)K_THIRD(c);
+ buf[len + 3] = NUL;
} else {
- buf[utf_char2bytes(c, buf)] = NUL;
- char_u *p = buf;
- while (*p) {
- if ((uint8_t)(*p) == CSI || (uint8_t)(*p) == K_SPECIAL) {
- bool is_csi = (uint8_t)(*p) == CSI;
- memmove(p + 3, p + 1, STRLEN(p + 1) + 1);
+ char_u *p = buf + len;
+ int char_len = utf_char2bytes(c, p);
+ len += char_len;
+ // If the character contains K_SPECIAL bytes they need escaping.
+ for (int i = char_len; --i >= 0; p++) {
+ if ((uint8_t)(*p) == K_SPECIAL) {
+ memmove(p + 3, p + 1, (size_t)i);
*p++ = K_SPECIAL;
- *p++ = is_csi ? KS_EXTRA : KS_SPECIAL;
- *p++ = is_csi ? KE_CSI : KE_FILLER;
- } else {
- p++;
+ *p++ = KS_SPECIAL;
+ *p = KE_FILLER;
+ len += 2;
}
}
+ *p = NUL;
}
(void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent);
+ return len;
}
/// Return TRUE if the typeahead buffer was changed (while waiting for a
@@ -1181,6 +1180,18 @@ static void gotchars(const char_u *chars, size_t len)
maptick++;
}
+/// Undo the last gotchars() for "len" bytes. To be used when putting a typed
+/// character back into the typeahead buffer, thus gotchars() will be called
+/// again.
+/// Only affects recorded characters.
+void ungetchars(int len)
+{
+ if (reg_recording != 0) {
+ delete_buff_tail(&recordbuff, len);
+ last_recorded_len -= (size_t)len;
+ }
+}
+
/*
* Sync undo. Called when typed characters are obtained from the typeahead
* buffer, or when a menu is used.
@@ -1427,15 +1438,13 @@ static void updatescript(int c)
}
}
-/*
- * Get the next input character.
- * Can return a special key or a multi-byte character.
- * Can return NUL when called recursively, use safe_vgetc() if that's not
- * wanted.
- * This translates escaped K_SPECIAL and CSI bytes to a K_SPECIAL or CSI byte.
- * Collects the bytes of a multibyte character into the whole character.
- * Returns the modifiers in the global "mod_mask".
- */
+/// Get the next input character.
+/// Can return a special key or a multi-byte character.
+/// Can return NUL when called recursively, use safe_vgetc() if that's not
+/// wanted.
+/// This translates escaped K_SPECIAL bytes to a K_SPECIAL byte.
+/// Collects the bytes of a multibyte character into the whole character.
+/// Returns the modifiers in the global "mod_mask".
int vgetc(void)
{
int c, c2;
@@ -1461,8 +1470,9 @@ int vgetc(void)
mouse_row = old_mouse_row;
mouse_col = old_mouse_col;
} else {
- mod_mask = 0x0;
+ mod_mask = 0;
last_recorded_len = 0;
+
for (;;) { // this is done twice if there are modifiers
bool did_inc = false;
if (mod_mask) { // no mapping after modifier has been read
@@ -1572,14 +1582,9 @@ int vgetc(void)
buf[i] = (char_u)vgetorpeek(true);
if (buf[i] == K_SPECIAL) {
// Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence,
- // which represents a K_SPECIAL (0x80),
- // or a CSI - KS_EXTRA - KE_CSI sequence, which represents
- // a CSI (0x9B),
- // of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too.
- c = vgetorpeek(true);
- if (vgetorpeek(true) == KE_CSI && c == KS_EXTRA) {
- buf[i] = CSI;
- }
+ // which represents a K_SPECIAL (0x80).
+ (void)vgetorpeek(true); // skip KS_SPECIAL
+ (void)vgetorpeek(true); // skip KE_FILLER
}
}
no_mapping--;
@@ -1593,8 +1598,8 @@ int vgetc(void)
if (!no_mapping && KeyTyped && !(State & TERM_FOCUS)
&& (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) {
mod_mask = 0;
- ins_char_typebuf(c);
- ins_char_typebuf(ESC);
+ ins_char_typebuf(c, 0);
+ ins_char_typebuf(ESC, 0);
continue;
}
@@ -2043,7 +2048,7 @@ void vungetc(int c)
///
/// When `no_mapping` (global) is zero, checks for mappings in the current mode.
/// Only returns one byte (of a multi-byte character).
-/// K_SPECIAL and CSI may be escaped, need to get two more bytes then.
+/// K_SPECIAL may be escaped, need to get two more bytes then.
static int vgetorpeek(bool advance)
{
int c, c1;
@@ -2573,7 +2578,7 @@ int fix_input_buffer(char_u *buf, int len)
FUNC_ATTR_NONNULL_ALL
{
if (!using_script()) {
- // Should not escape K_SPECIAL/CSI reading input from the user because vim
+ // Should not escape K_SPECIAL reading input from the user because vim
// key codes keys are processed in input.c/input_enqueue.
buf[len] = NUL;
return len;
@@ -2584,9 +2589,8 @@ int fix_input_buffer(char_u *buf, int len)
char_u *p = buf;
// Two characters are special: NUL and K_SPECIAL.
- // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER
+ // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER
// Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER
- // Replace CSI by K_SPECIAL KS_EXTRA KE_CSI
for (i = len; --i >= 0; ++p) {
if (p[0] == NUL
|| (p[0] == K_SPECIAL
@@ -3477,10 +3481,10 @@ static void showmap(mapblock_T *mp, bool local)
} else if (mp->m_str == NULL) {
msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
} else {
- // Remove escaping of CSI, because "m_str" is in a format to be used
+ // Remove escaping of K_SPECIAL, because "m_str" is in a format to be used
// as typeahead.
char_u *s = vim_strsave(mp->m_str);
- vim_unescape_csi(s);
+ vim_unescape_ks(s);
msg_outtrans_special(s, false, 0);
xfree(s);
}
@@ -3860,9 +3864,9 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
int match;
if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) {
- // Might have CSI escaped mp->m_keys.
+ // Might have K_SPECIAL escaped mp->m_keys.
q = vim_strsave(mp->m_keys);
- vim_unescape_csi(q);
+ vim_unescape_ks(q);
qlen = (int)STRLEN(q);
}
// find entries with right mode and keys
@@ -3908,7 +3912,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
int newlen = utf_char2bytes(c, tb + j);
tb[j + newlen] = NUL;
// Need to escape K_SPECIAL.
- char_u *escaped = vim_strsave_escape_csi(tb + j);
+ char_u *escaped = vim_strsave_escape_ks(tb + j);
if (escaped != NULL) {
newlen = (int)STRLEN(escaped);
memmove(tb + j, escaped, (size_t)newlen);
@@ -3961,11 +3965,11 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
int save_msg_col;
int save_msg_row;
- /* Remove escaping of CSI, because "str" is in a format to be used as
- * typeahead. */
+ // Remove escaping of K_SPECIAL, because "str" is in a format to be used as
+ // typeahead.
if (mp->m_luaref == LUA_NOREF) {
expr = vim_strsave(mp->m_str);
- vim_unescape_csi(expr);
+ vim_unescape_ks(expr);
}
save_cmd = save_cmdline_alloc();
@@ -4005,18 +4009,16 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
if (p == NULL) {
return NULL;
}
- // Escape CSI in the result to be able to use the string as typeahead.
- res = vim_strsave_escape_csi(p);
+ // Escape K_SPECIAL in the result to be able to use the string as typeahead.
+ res = vim_strsave_escape_ks(p);
xfree(p);
return res;
}
-/*
- * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
- * can be put in the typeahead buffer.
- */
-char_u *vim_strsave_escape_csi(char_u *p)
+/// Copy "p" to allocated memory, escaping K_SPECIAL so that the result
+/// can be put in the typeahead buffer.
+char_u *vim_strsave_escape_ks(char_u *p)
{
// Need a buffer to hold up to three times as much. Four in case of an
// illegal utf-8 byte:
@@ -4031,7 +4033,7 @@ char_u *vim_strsave_escape_csi(char_u *p)
*d++ = *s++;
} else {
// Add character, possibly multi-byte to destination, escaping
- // CSI and K_SPECIAL. Be careful, it can be an illegal byte!
+ // K_SPECIAL. Be careful, it can be an illegal byte!
d = add_char2buf(utf_ptr2char(s), d);
s += utf_ptr2len(s);
}
@@ -4041,11 +4043,9 @@ char_u *vim_strsave_escape_csi(char_u *p)
return res;
}
-/*
- * Remove escaping from CSI and K_SPECIAL characters. Reverse of
- * vim_strsave_escape_csi(). Works in-place.
- */
-void vim_unescape_csi(char_u *p)
+/// Remove escaping from K_SPECIAL characters. Reverse of
+/// vim_strsave_escape_ks(). Works in-place.
+void vim_unescape_ks(char_u *p)
{
char_u *s = p, *d = p;
@@ -4053,10 +4053,6 @@ void vim_unescape_csi(char_u *p)
if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) {
*d++ = K_SPECIAL;
s += 3;
- } else if ((s[0] == K_SPECIAL || s[0] == CSI)
- && s[1] == KS_EXTRA && s[2] == (int)KE_CSI) {
- *d++ = CSI;
- s += 3;
} else {
*d++ = *s++;
}
@@ -4299,7 +4295,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what)
for (; *str != NUL; str++) {
// Check for a multi-byte character, which may contain escaped
- // K_SPECIAL and CSI bytes.
+ // K_SPECIAL bytes.
const char *p = mb_unescape((const char **)&str);
if (p != NULL) {
while (*p != NUL) {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 40c61d01b5..041b60d838 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -127,7 +127,7 @@ typedef off_t off_T;
// When vgetc() is called, it sets mod_mask to the set of modifiers that are
// held down based on the MOD_MASK_* symbols that are read first.
-EXTERN int mod_mask INIT(= 0x0); // current key modifiers
+EXTERN int mod_mask INIT(= 0); // current key modifiers
// Cmdline_row is the row where the command line starts, just below the
@@ -524,6 +524,8 @@ EXTERN pos_T VIsual;
EXTERN int VIsual_active INIT(= false);
/// Whether Select mode is active.
EXTERN int VIsual_select INIT(= false);
+/// Register name for Select mode
+EXTERN int VIsual_select_reg INIT(= 0);
/// Restart Select mode when next cmd finished
EXTERN int restart_VIsual_select INIT(= 0);
/// Whether to restart the selection after a Select-mode mapping or menu.
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 2f7c5c2c16..5fa9b8b343 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -105,7 +105,7 @@ int get_keystroke(MultiQueue *events)
// 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.
+ // Replace zero and K_SPECIAL by a special key code.
n = fix_input_buffer(buf + len, n);
len += n;
waited = 0;
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index abf016b832..32f2158d7b 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -158,7 +158,6 @@ static const struct key_name_entry {
{ ESC, "Esc" },
{ ESC, "Escape" }, // Alternative name
{ CSI, "CSI" },
- { K_CSI, "xCSI" },
{ '|', "Bar" },
{ '\\', "Bslash" },
{ K_DEL, "Del" },
@@ -964,7 +963,6 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
for (i = utfc_ptr2len_len(src, (int)(end - src) + 1); i > 0; i--) {
// If the character is K_SPECIAL, replace it with K_SPECIAL
// KS_SPECIAL KE_FILLER.
- // If compiled with the GUI replace CSI with K_CSI.
if (*src == K_SPECIAL) {
result[dlen++] = K_SPECIAL;
result[dlen++] = KS_SPECIAL;
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index 5a74d1dc00..42cae0c35e 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -220,7 +220,7 @@ enum key_extra {
KE_KINS = 79, // keypad Insert key
KE_KDEL = 80, // keypad Delete key
- KE_CSI = 81, // CSI typed directly
+ // KE_CSI = 81, // Nvim doesn't need escaping CSI
KE_SNR = 82, // <SNR>
KE_PLUG = 83, // <Plug>
KE_CMDWIN = 84, // open command-line window from Command-line Mode
@@ -435,7 +435,6 @@ enum key_extra {
#define K_MOUSELEFT TERMCAP2KEY(KS_EXTRA, KE_MOUSELEFT)
#define K_MOUSERIGHT TERMCAP2KEY(KS_EXTRA, KE_MOUSERIGHT)
-#define K_CSI TERMCAP2KEY(KS_EXTRA, KE_CSI)
#define K_SNR TERMCAP2KEY(KS_EXTRA, KE_SNR)
#define K_PLUG TERMCAP2KEY(KS_EXTRA, KE_PLUG)
#define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN)
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 8a702ddd60..0fbd56ed53 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -617,6 +617,14 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special)
semsg(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
return false;
}
+ if (tv->v_type == VAR_FUNC) {
+ ufunc_T *fp = find_func(tv->vval.v_string);
+ assert(fp != NULL);
+ if (fp->uf_cb == nlua_CFunction_func_call) {
+ nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref);
+ return true;
+ }
+ }
if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) {
return false;
}
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index cfdbe7b344..5c4d7e3c91 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -4,6 +4,7 @@
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
+#include <tree_sitter/api.h>
#include "luv/luv.h"
#include "nvim/api/private/defs.h"
@@ -1267,6 +1268,12 @@ int tslua_get_language_version(lua_State *L)
return 1;
}
+int tslua_get_minimum_language_version(lua_State *L)
+{
+ lua_pushnumber(L, TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
+ return 1;
+}
+
static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
tslua_init(lstate);
@@ -1288,6 +1295,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, tslua_get_language_version);
lua_setfield(lstate, -2, "_ts_get_language_version");
+
+ lua_pushcfunction(lstate, tslua_get_minimum_language_version);
+ lua_setfield(lstate, -2, "_ts_get_minimum_language_version");
}
int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***results)
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 60a000843f..f4067ad02f 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -129,6 +129,10 @@ void tslua_init(lua_State *L)
build_meta(L, TS_META_QUERY, query_meta);
build_meta(L, TS_META_QUERYCURSOR, querycursor_meta);
build_meta(L, TS_META_TREECURSOR, treecursor_meta);
+
+#ifdef NVIM_TS_HAS_SET_ALLOCATOR
+ ts_set_allocator(xmalloc, xcalloc, xrealloc, xfree);
+#endif
}
int tslua_has_language(lua_State *L)
diff --git a/src/nvim/main.c b/src/nvim/main.c
index cbd1f53727..748f5098fd 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -341,9 +341,11 @@ int main(int argc, char **argv)
init_default_autocmds();
TIME_MSG("init default autocommands");
+ bool vimrc_none = params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE");
+
// Reset 'loadplugins' for "-u NONE" before "--cmd" arguments.
// Allows for setting 'loadplugins' there.
- if (params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE")) {
+ if (vimrc_none) {
// When using --clean we still want to load plugins
p_lpl = params.clean;
}
@@ -351,14 +353,23 @@ int main(int argc, char **argv)
// Execute --cmd arguments.
exe_pre_commands(&params);
+ if (!vimrc_none) {
+ // Sources ftplugin.vim and indent.vim. We do this *before* the user startup scripts to ensure
+ // ftplugins run before FileType autocommands defined in the init file (which allows those
+ // autocommands to overwrite settings from ftplugins).
+ filetype_plugin_enable();
+ }
+
// Source startup scripts.
source_startup_scripts(&params);
// If using the runtime (-u is not NONE), enable syntax & filetype plugins.
- if (params.use_vimrc == NULL || !strequal(params.use_vimrc, "NONE")) {
- // Does ":filetype plugin indent on".
+ if (!vimrc_none) {
+ // Sources filetype.lua and filetype.vim unless the user explicitly disabled it with :filetype
+ // off.
filetype_maybe_enable();
- // Sources syntax/syntax.vim, which calls `:filetype on`.
+ // Sources syntax/syntax.vim. We do this *after* the user startup scripts so that users can
+ // disable syntax highlighting with `:syntax off` if they wish.
syn_maybe_enable();
}
diff --git a/src/nvim/map.c b/src/nvim/map.c
index c77433df71..77ebc2a387 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -171,10 +171,7 @@ MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER)
MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER)
-#define EXTMARK_NS_INITIALIZER { { MAP_INIT }, 1 }
-MAP_IMPL(uint64_t, ExtmarkNs, EXTMARK_NS_INITIALIZER)
-#define EXTMARK_ITEM_INITIALIZER { 0, 0, NULL }
-MAP_IMPL(uint64_t, ExtmarkItem, EXTMARK_ITEM_INITIALIZER)
+MAP_IMPL(uint32_t, uint32_t, DEFAULT_INITIALIZER)
MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER)
#define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false }
MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index dbd85a4e1f..5e56f4dd65 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -40,16 +40,8 @@ MAP_DECLS(ptr_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
MAP_DECLS(uint64_t, ssize_t)
MAP_DECLS(uint64_t, uint64_t)
+MAP_DECLS(uint32_t, uint32_t)
-// NB: this is the only way to define a struct both containing and contained
-// in a map...
-typedef struct ExtmarkNs { // For namespacing extmarks
- Map(uint64_t, uint64_t) map[1]; // For fast lookup
- uint64_t free_id; // For automatically assigning id's
-} ExtmarkNs;
-
-MAP_DECLS(uint64_t, ExtmarkNs)
-MAP_DECLS(uint64_t, ExtmarkItem)
MAP_DECLS(handle_T, ptr_t)
MAP_DECLS(String, MsgpackRpcRequestHandler)
MAP_DECLS(HlEntry, int)
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 38014ab375..918db8b76c 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -56,12 +56,6 @@
#define T MT_BRANCH_FACTOR
#define ILEN (sizeof(mtnode_t)+(2 * T) * sizeof(void *))
-#define RIGHT_GRAVITY (((uint64_t)1) << 63)
-#define ANTIGRAVITY(id) ((id)&(RIGHT_GRAVITY-1))
-#define IS_RIGHT(id) ((id)&RIGHT_GRAVITY)
-
-#define PAIRED MARKTREE_PAIRED_FLAG
-#define END_FLAG MARKTREE_END_FLAG
#define ID_INCR (((uint64_t)1) << 2)
#define rawkey(itr) (itr->node->key[itr->i])
@@ -119,7 +113,7 @@ static int key_cmp(mtkey_t a, mtkey_t b)
}
// NB: keeping the events at the same pos sorted by id is actually not
// necessary only make sure that START is before END etc.
- return mt_generic_cmp(a.id, b.id);
+ return mt_generic_cmp(a.flags, b.flags);
}
static inline int marktree_getp_aux(const mtnode_t *x, mtkey_t k, int *r)
@@ -148,7 +142,7 @@ static inline int marktree_getp_aux(const mtnode_t *x, mtkey_t k, int *r)
static inline void refkey(MarkTree *b, mtnode_t *x, int i)
{
- pmap_put(uint64_t)(b->id2node, ANTIGRAVITY(x->key[i].id), x);
+ pmap_put(uint64_t)(b->id2node, mt_lookup_key(x->key[i]), x);
}
// put functions
@@ -221,38 +215,28 @@ static inline void marktree_putp_aux(MarkTree *b, mtnode_t *x, mtkey_t k)
}
}
-uint64_t marktree_put(MarkTree *b, int row, int col, bool right_gravity, uint8_t decor_level)
+void marktree_put(MarkTree *b, mtkey_t key, int end_row, int end_col, bool end_right)
{
- uint64_t id = (b->next_id+=ID_INCR);
- assert(decor_level < DECOR_LEVELS);
- id = id | ((uint64_t)decor_level << DECOR_OFFSET);
- uint64_t keyid = id;
- if (right_gravity) {
- // order all right gravity keys after the left ones, for effortless
- // insertion (but not deletion!)
- keyid |= RIGHT_GRAVITY;
- }
- marktree_put_key(b, row, col, keyid);
- return id;
-}
+ assert(!(key.flags & ~MT_FLAG_EXTERNAL_MASK));
+ if (end_row >= 0) {
+ key.flags |= MT_FLAG_PAIRED;
+ }
-uint64_t marktree_put_pair(MarkTree *b, int start_row, int start_col, bool start_right, int end_row,
- int end_col, bool end_right, uint8_t decor_level)
-{
- uint64_t id = (b->next_id+=ID_INCR)|PAIRED;
- assert(decor_level < DECOR_LEVELS);
- id = id | ((uint64_t)decor_level << DECOR_OFFSET);
- uint64_t start_id = id|(start_right?RIGHT_GRAVITY:0);
- uint64_t end_id = id|END_FLAG|(end_right?RIGHT_GRAVITY:0);
- marktree_put_key(b, start_row, start_col, start_id);
- marktree_put_key(b, end_row, end_col, end_id);
- return id;
+ marktree_put_key(b, key);
+
+ if (end_row >= 0) {
+ mtkey_t end_key = key;
+ end_key.flags = (uint16_t)((uint16_t)(key.flags & ~MT_FLAG_RIGHT_GRAVITY)
+ |(uint16_t)MT_FLAG_END
+ |(uint16_t)(end_right ? MT_FLAG_RIGHT_GRAVITY : 0));
+ end_key.pos = (mtpos_t){ end_row, end_col };
+ marktree_put_key(b, end_key);
+ }
}
-void marktree_put_key(MarkTree *b, int row, int col, uint64_t id)
+void marktree_put_key(MarkTree *b, mtkey_t k)
{
- mtkey_t k = { .pos = { .row = row, .col = col }, .id = id };
-
+ k.flags |= MT_FLAG_REAL; // let's be real.
if (!b->root) {
b->root = (mtnode_t *)xcalloc(1, ILEN);
b->n_nodes++;
@@ -302,7 +286,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
mtnode_t *cur = itr->node;
int curi = itr->i;
- uint64_t id = cur->key[curi].id;
+ uint64_t id = mt_lookup_key(cur->key[curi]);
// fprintf(stderr, "\nDELET %lu\n", id);
if (itr->node->level) {
@@ -364,7 +348,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
}
b->n_keys--;
- pmap_del(uint64_t)(b->id2node, ANTIGRAVITY(id));
+ pmap_del(uint64_t)(b->id2node, id);
// 5.
bool itr_dirty = false;
@@ -570,23 +554,29 @@ void marktree_free_node(mtnode_t *x)
}
/// NB: caller must check not pair!
-uint64_t marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level)
+void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_t key)
{
- uint64_t old_id = rawkey(itr).id;
- pmap_del(uint64_t)(b->id2node, ANTIGRAVITY(old_id));
- uint64_t new_id = (b->next_id += ID_INCR) + ((uint64_t)decor_level << DECOR_OFFSET);
- rawkey(itr).id = new_id + (RIGHT_GRAVITY&old_id);
- refkey(b, itr->node, itr->i);
- return new_id;
+ // TODO(bfredl): clean up this mess and re-instantiate &= and |= forms
+ // once we upgrade to a non-broken version of gcc in functionaltest-lua CI
+ rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK);
+ rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags
+ | (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET)
+ | (uint16_t)(key.flags & MT_FLAG_DECOR_MASK));
+ rawkey(itr).decor_full = key.decor_full;
+ rawkey(itr).hl_id = key.hl_id;
+ rawkey(itr).priority = key.priority;
}
void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col)
{
- uint64_t old_id = rawkey(itr).id;
+ mtkey_t key = rawkey(itr);
// TODO(bfredl): optimize when moving a mark within a leaf without moving it
// across neighbours!
marktree_del_itr(b, itr, false);
- marktree_put_key(b, row, col, old_id);
+ key.pos = (mtpos_t){ row, col };
+
+
+ marktree_put_key(b, key);
itr->node = NULL; // itr might become invalid by put
}
@@ -602,14 +592,15 @@ bool marktree_itr_get(MarkTree *b, int row, int col, MarkTreeIter *itr)
bool marktree_itr_get_ext(MarkTree *b, mtpos_t p, MarkTreeIter *itr, bool last, bool gravity,
mtpos_t *oldbase)
{
- mtkey_t k = { .pos = p, .id = gravity ? RIGHT_GRAVITY : 0 };
- if (last && !gravity) {
- k.id = UINT64_MAX;
- }
if (b->n_keys == 0) {
itr->node = NULL;
return false;
}
+
+ mtkey_t k = { .pos = p, .flags = gravity ? MT_FLAG_RIGHT_GRAVITY : 0 };
+ if (last && !gravity) {
+ k.flags = MT_FLAG_LAST;
+ }
itr->pos = (mtpos_t){ 0, 0 };
itr->node = b->root;
itr->lvl = 0;
@@ -816,25 +807,29 @@ mtpos_t marktree_itr_pos(MarkTreeIter *itr)
return pos;
}
-mtmark_t marktree_itr_current(MarkTreeIter *itr)
+mtkey_t marktree_itr_current(MarkTreeIter *itr)
{
if (itr->node) {
- uint64_t keyid = rawkey(itr).id;
- mtpos_t pos = marktree_itr_pos(itr);
- mtmark_t mark = { .row = pos.row,
- .col = pos.col,
- .id = ANTIGRAVITY(keyid),
- .right_gravity = keyid & RIGHT_GRAVITY };
- return mark;
- }
- return (mtmark_t){ -1, -1, 0, false };
+ mtkey_t key = rawkey(itr);
+ key.pos = marktree_itr_pos(itr);
+ return key;
+ }
+ return MT_INVALID_KEY;
+}
+
+static bool itr_eq(MarkTreeIter *itr1, MarkTreeIter *itr2)
+{
+ return (&rawkey(itr1) == &rawkey(itr2));
}
-static void swap_id(uint64_t *id1, uint64_t *id2)
+static void itr_swap(MarkTreeIter *itr1, MarkTreeIter *itr2)
{
- uint64_t temp = *id1;
- *id1 = *id2;
- *id2 = temp;
+ mtkey_t key1 = rawkey(itr1);
+ mtkey_t key2 = rawkey(itr2);
+ rawkey(itr1) = key2;
+ rawkey(itr1).pos = key1.pos;
+ rawkey(itr2) = key1;
+ rawkey(itr2).pos = key2.pos;
}
bool marktree_splice(MarkTree *b, int start_line, int start_col, int old_extent_line,
@@ -865,7 +860,7 @@ bool marktree_splice(MarkTree *b, int start_line, int start_col, int old_extent_
mtpos_t ipos = marktree_itr_pos(itr);
if (!pos_leq(old_extent, ipos)
|| (old_extent.row == ipos.row && old_extent.col == ipos.col
- && !IS_RIGHT(rawkey(itr).id))) {
+ && !mt_right(rawkey(itr)))) {
marktree_itr_get_ext(b, old_extent, enditr, true, true, NULL);
assert(enditr->node);
// "assert" (itr <= enditr)
@@ -895,13 +890,13 @@ continue_same_node:
break;
}
- if (IS_RIGHT(rawkey(itr).id)) {
- while (rawkey(itr).id != rawkey(enditr).id
- && IS_RIGHT(rawkey(enditr).id)) {
+ if (mt_right(rawkey(itr))) {
+ while (!itr_eq(itr, enditr)
+ && mt_right(rawkey(enditr))) {
marktree_itr_prev(b, enditr);
}
- if (!IS_RIGHT(rawkey(enditr).id)) {
- swap_id(&rawkey(itr).id, &rawkey(enditr).id);
+ if (!mt_right(rawkey(enditr))) {
+ itr_swap(itr, enditr);
refkey(b, itr->node, itr->i);
refkey(b, enditr->node, enditr->i);
} else {
@@ -911,7 +906,7 @@ continue_same_node:
}
}
- if (rawkey(itr).id == rawkey(enditr).id) {
+ if (itr_eq(itr, enditr)) {
// actually, will be past_right after this key
past_right = true;
}
@@ -1006,13 +1001,13 @@ void marktree_move_region(MarkTree *b, int start_row, colnr_T start_col, int ext
marktree_itr_get_ext(b, start, itr, false, true, NULL);
kvec_t(mtkey_t) saved = KV_INITIAL_VALUE;
while (itr->node) {
- mtpos_t pos = marktree_itr_pos(itr);
- if (!pos_leq(pos, end) || (pos.row == end.row && pos.col == end.col
- && rawkey(itr).id & RIGHT_GRAVITY)) {
+ mtkey_t k = marktree_itr_current(itr);
+ if (!pos_leq(k.pos, end) || (k.pos.row == end.row && k.pos.col == end.col
+ && mt_right(k))) {
break;
}
- relative(start, &pos);
- kv_push(saved, ((mtkey_t){ .pos = pos, .id = rawkey(itr).id }));
+ relative(start, &k.pos);
+ kv_push(saved, k);
marktree_del_itr(b, itr, false);
}
@@ -1024,30 +1019,36 @@ void marktree_move_region(MarkTree *b, int start_row, colnr_T start_col, int ext
for (size_t i = 0; i < kv_size(saved); i++) {
mtkey_t item = kv_A(saved, i);
unrelative(new, &item.pos);
- marktree_put_key(b, item.pos.row, item.pos.col, item.id);
+ marktree_put_key(b, item);
}
kv_destroy(saved);
}
/// @param itr OPTIONAL. set itr to pos.
-mtpos_t marktree_lookup(MarkTree *b, uint64_t id, MarkTreeIter *itr)
+mtkey_t marktree_lookup_ns(MarkTree *b, uint32_t ns, uint32_t id, bool end, MarkTreeIter *itr)
+{
+ return marktree_lookup(b, mt_lookup_id(ns, id, end), itr);
+}
+
+/// @param itr OPTIONAL. set itr to pos.
+mtkey_t marktree_lookup(MarkTree *b, uint64_t id, MarkTreeIter *itr)
{
mtnode_t *n = pmap_get(uint64_t)(b->id2node, id);
if (n == NULL) {
if (itr) {
itr->node = NULL;
}
- return (mtpos_t){ -1, -1 };
+ return MT_INVALID_KEY;
}
int i = 0;
for (i = 0; i < n->n; i++) {
- if (ANTIGRAVITY(n->key[i].id) == id) {
+ if (mt_lookup_key(n->key[i]) == id) {
goto found;
}
}
abort();
found: {}
- mtpos_t pos = n->key[i].pos;
+ mtkey_t key = n->key[i];
if (itr) {
itr->i = i;
itr->node = n;
@@ -1066,14 +1067,28 @@ found_node:
itr->s[b->root->level-p->level].i = i;
}
if (i > 0) {
- unrelative(p->key[i-1].pos, &pos);
+ unrelative(p->key[i-1].pos, &key.pos);
}
n = p;
}
if (itr) {
marktree_itr_fix_pos(b, itr);
}
- return pos;
+ return key;
+}
+
+mtpos_t marktree_get_altpos(MarkTree *b, mtkey_t mark, MarkTreeIter *itr)
+{
+ return marktree_get_alt(b, mark, itr).pos;
+}
+
+mtkey_t marktree_get_alt(MarkTree *b, mtkey_t mark, MarkTreeIter *itr)
+{
+ mtkey_t end = MT_INVALID_KEY;
+ if (mt_paired(mark)) {
+ end = marktree_lookup_ns(b, mark.ns, mark.id, !mt_end(mark), itr);
+ }
+ return end;
}
static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr)
@@ -1092,6 +1107,20 @@ static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr)
assert(x == itr->node);
}
+// for unit test
+void marktree_put_test(MarkTree *b, uint32_t id, int row, int col, bool right_gravity)
+{
+ mtkey_t key = { { row, col }, UINT32_MAX, id, 0,
+ mt_flags(right_gravity, 0), 0, NULL };
+ marktree_put(b, key, -1, -1, false);
+}
+
+// for unit test
+bool mt_right_test(mtkey_t key)
+{
+ return mt_right(key);
+}
+
void marktree_check(MarkTree *b)
{
#ifndef NDEBUG
@@ -1134,11 +1163,11 @@ static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_rig
}
assert(pos_leq(*last, x->key[i].pos));
if (last->row == x->key[i].pos.row && last->col == x->key[i].pos.col) {
- assert(!*last_right || IS_RIGHT(x->key[i].id));
+ assert(!*last_right || mt_right(x->key[i]));
}
- *last_right = IS_RIGHT(x->key[i].id);
+ *last_right = mt_right(x->key[i]);
assert(x->key[i].pos.col >= 0);
- assert(pmap_get(uint64_t)(b->id2node, ANTIGRAVITY(x->key[i].id)) == x);
+ assert(pmap_get(uint64_t)(b->id2node, mt_lookup_key(x->key[i])) == x);
}
if (x->level) {
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index a1dcdf5164..30f5aacebc 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -2,9 +2,12 @@
#define NVIM_MARKTREE_H
#include <stdint.h>
+#include <assert.h>
+#include "nvim/assert.h"
#include "nvim/garray.h"
#include "nvim/map.h"
+#include "nvim/types.h"
#include "nvim/pos.h"
#define MT_MAX_DEPTH 20
@@ -15,13 +18,6 @@ typedef struct {
int32_t col;
} mtpos_t;
-typedef struct {
- int32_t row;
- int32_t col;
- uint64_t id;
- bool right_gravity;
-} mtmark_t;
-
typedef struct mtnode_s mtnode_t;
typedef struct {
int oldcol;
@@ -39,12 +35,75 @@ typedef struct {
// Internal storage
//
-// NB: actual marks have id > 0, so we can use (row,col,0) pseudo-key for
+// NB: actual marks have flags > 0, so we can use (row,col,0) pseudo-key for
// "space before (row,col)"
typedef struct {
mtpos_t pos;
- uint64_t id;
+ uint32_t ns;
+ uint32_t id;
+ int32_t hl_id;
+ uint16_t flags;
+ uint16_t priority;
+ Decoration *decor_full;
} mtkey_t;
+#define MT_INVALID_KEY (mtkey_t) { { -1, -1 }, 0, 0, 0, 0, 0, NULL }
+
+#define MT_FLAG_REAL (((uint16_t)1) << 0)
+#define MT_FLAG_END (((uint16_t)1) << 1)
+#define MT_FLAG_PAIRED (((uint16_t)1) << 2)
+#define MT_FLAG_HL_EOL (((uint16_t)1) << 3)
+
+#define DECOR_LEVELS 4
+#define MT_FLAG_DECOR_OFFSET 4
+#define MT_FLAG_DECOR_MASK (((uint16_t)(DECOR_LEVELS-1)) << MT_FLAG_DECOR_OFFSET)
+
+// next flag is (((uint16_t)1) << 6)
+
+// These _must_ be last to preserve ordering of marks
+#define MT_FLAG_RIGHT_GRAVITY (((uint16_t)1) << 14)
+#define MT_FLAG_LAST (((uint16_t)1) << 15)
+
+#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_RIGHT_GRAVITY | MT_FLAG_HL_EOL)
+
+#define MARKTREE_END_FLAG (((uint64_t)1) << 63)
+static inline uint64_t mt_lookup_id(uint32_t ns, uint32_t id, bool enda)
+{
+ return (uint64_t)ns << 32 | id | (enda?MARKTREE_END_FLAG:0);
+}
+#undef MARKTREE_END_FLAG
+
+static inline uint64_t mt_lookup_key(mtkey_t key)
+{
+ return mt_lookup_id(key.ns, key.id, key.flags & MT_FLAG_END);
+}
+
+static inline bool mt_paired(mtkey_t key)
+{
+ return key.flags & MT_FLAG_PAIRED;
+}
+
+static inline bool mt_end(mtkey_t key)
+{
+ return key.flags & MT_FLAG_END;
+}
+
+static inline bool mt_right(mtkey_t key)
+{
+ return key.flags & MT_FLAG_RIGHT_GRAVITY;
+}
+
+static inline uint8_t marktree_decor_level(mtkey_t key)
+{
+ return (uint8_t)((key.flags&MT_FLAG_DECOR_MASK) >> MT_FLAG_DECOR_OFFSET);
+}
+
+static inline uint16_t mt_flags(bool right_gravity, uint8_t decor_level)
+{
+ assert(decor_level < DECOR_LEVELS);
+ return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0)
+ | (decor_level << MT_FLAG_DECOR_OFFSET));
+}
+
struct mtnode_s {
int32_t n;
@@ -61,7 +120,6 @@ struct mtnode_s {
typedef struct {
mtnode_t *root;
size_t n_keys, n_nodes;
- uint64_t next_id;
// TODO(bfredl): the pointer to node could be part of the larger
// Map(uint64_t, ExtmarkItem) essentially;
PMap(uint64_t) id2node[1];
@@ -72,16 +130,4 @@ typedef struct {
# include "marktree.h.generated.h"
#endif
-#define MARKTREE_PAIRED_FLAG (((uint64_t)1) << 1)
-#define MARKTREE_END_FLAG (((uint64_t)1) << 0)
-
-#define DECOR_LEVELS 4
-#define DECOR_OFFSET 61
-#define DECOR_MASK (((uint64_t)(DECOR_LEVELS-1)) << DECOR_OFFSET)
-
-static inline uint8_t marktree_decor_level(uint64_t id)
-{
- return (uint8_t)((id&DECOR_MASK) >> DECOR_OFFSET);
-}
-
#endif // NVIM_MARKTREE_H
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 5eb209a6f6..7fa2562be0 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1067,7 +1067,7 @@ bool utf_printable(int c)
static struct interval nonprint[] =
{
{ 0x070f, 0x070f }, { 0x180b, 0x180e }, { 0x200b, 0x200f }, { 0x202a, 0x202e },
- { 0x206a, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb },
+ { 0x2060, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb },
{ 0xfffe, 0xffff }
};
@@ -2089,8 +2089,7 @@ const char *mb_unescape(const char **const pp)
size_t buf_idx = 0;
uint8_t *str = (uint8_t *)(*pp);
- // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI
- // KS_EXTRA KE_CSI to CSI.
+ // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL.
// Maximum length of a utf-8 character is 4 bytes.
for (size_t str_idx = 0; str[str_idx] != NUL && buf_idx < 4; str_idx++) {
if (str[str_idx] == K_SPECIAL
@@ -2098,11 +2097,6 @@ const char *mb_unescape(const char **const pp)
&& str[str_idx + 2] == KE_FILLER) {
buf[buf_idx++] = (char)K_SPECIAL;
str_idx += 2;
- } else if ((str[str_idx] == K_SPECIAL)
- && str[str_idx + 1] == KS_EXTRA
- && str[str_idx + 2] == KE_CSI) {
- buf[buf_idx++] = (char)CSI;
- str_idx += 2;
} else if (str[str_idx] == K_SPECIAL) {
break; // A special key can't be a multibyte char.
} else {
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 3397296b3a..2a72d1e6a0 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -247,7 +247,7 @@ bhdr_T *mf_new(memfile_T *mfp, bool negative, unsigned page_count)
} else { // need to allocate memory for this block
// If the number of pages matches use the bhdr_T from the free list and
// allocate the data.
- void *p = xmalloc(mfp->mf_page_size * page_count);
+ void *p = xmalloc((size_t)mfp->mf_page_size * page_count);
hp = mf_rem_free(mfp);
hp->bh_data = p;
}
@@ -269,7 +269,7 @@ bhdr_T *mf_new(memfile_T *mfp, bool negative, unsigned page_count)
// Init the data to all zero, to avoid reading uninitialized data.
// This also avoids that the passwd file ends up in the swap file!
- (void)memset(hp->bh_data, 0, mfp->mf_page_size * page_count);
+ (void)memset(hp->bh_data, 0, (size_t)mfp->mf_page_size * page_count);
return hp;
}
@@ -528,7 +528,7 @@ bool mf_release_all(void)
static bhdr_T *mf_alloc_bhdr(memfile_T *mfp, unsigned page_count)
{
bhdr_T *hp = xmalloc(sizeof(bhdr_T));
- hp->bh_data = xmalloc(mfp->mf_page_size * page_count);
+ hp->bh_data = xmalloc((size_t)mfp->mf_page_size * page_count);
hp->bh_page_count = page_count;
return hp;
}
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 08521c0dc3..9925971783 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1032,9 +1032,9 @@ void ml_recover(bool checkext)
line_count = 0;
idx = 0; // start with first index in block 1
error = 0;
- buf->b_ml.ml_stack_top = 0;
+ buf->b_ml.ml_stack_top = 0; // -V1048
buf->b_ml.ml_stack = NULL;
- buf->b_ml.ml_stack_size = 0; // no stack yet
+ buf->b_ml.ml_stack_size = 0; // -V1048
if (curbuf->b_ffname == NULL) {
cannot_open = true;
@@ -4139,7 +4139,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff)
|| (offset != 0
&& offset > size +
buf->b_ml.ml_chunksize[curix].mlcs_totalsize
- + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines))) {
+ + (long)ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines))) {
curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
if (offset && ffdos) {
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 39b023132e..e1e253cd2e 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1215,7 +1215,7 @@ void wait_return(int redraw)
} else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C) {
// Put the character back in the typeahead buffer. Don't use the
// stuff buffer, because lmaps wouldn't work.
- ins_char_typebuf(c);
+ ins_char_typebuf(c, mod_mask);
do_redraw = true; // need a redraw even though there is
// typeahead
}
@@ -2647,6 +2647,17 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
char buf[7];
char *p;
+ if (on_print.type != kCallbackNone) {
+ typval_T argv[1];
+ argv[0].v_type = VAR_STRING;
+ argv[0].v_lock = VAR_UNLOCKED;
+ argv[0].vval.v_string = (char_u *)str;
+ typval_T rettv = TV_INITIAL_VALUE;
+ callback_call(&on_print, 1, argv, &rettv);
+ tv_clear(&rettv);
+ return;
+ }
+
while ((maxlen < 0 || s - str < maxlen) && *s != NUL) {
int len = utf_ptr2len((const char_u *)s);
if (!(silent_mode && p_verbose == 0)) {
@@ -3497,7 +3508,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
}
if (c == ':' && ex_cmd) {
retval = dfltbutton;
- ins_char_typebuf(':');
+ ins_char_typebuf(':', 0);
break;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 60bf393085..28e5d47dbc 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -164,7 +164,7 @@ static const struct nv_cmd {
{ Ctrl_O, nv_ctrlo, 0, 0 },
{ Ctrl_P, nv_up, NV_STS, false },
{ Ctrl_Q, nv_visual, 0, false },
- { Ctrl_R, nv_redo, 0, 0 },
+ { Ctrl_R, nv_redo_or_register, 0, 0 },
{ Ctrl_S, nv_ignore, 0, 0 },
{ Ctrl_T, nv_tagpop, NV_NCW, 0 },
{ Ctrl_U, nv_halfpage, 0, 0 },
@@ -961,6 +961,7 @@ normal_end:
&& s->oa.regname == 0) {
if (restart_VIsual_select == 1) {
VIsual_select = true;
+ VIsual_select_reg = 0;
trigger_modechanged();
showmode();
restart_VIsual_select = 0;
@@ -1010,7 +1011,14 @@ static int normal_execute(VimState *state, int key)
// restart automatically.
// Insert the typed character in the typeahead buffer, so that it can
// be mapped in Insert mode. Required for ":lmap" to work.
- ins_char_typebuf(s->c);
+ int len = ins_char_typebuf(s->c, mod_mask);
+
+ // When recording and gotchars() was called the character will be
+ // recorded again, remove the previous recording.
+ if (KeyTyped) {
+ ungetchars(len);
+ }
+
if (restart_edit != 0) {
s->c = 'd';
} else {
@@ -3281,7 +3289,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
int col_off1; // margin offset for first screen line
int col_off2; // margin offset for wrapped screen line
int width1; // text width for first screen line
- int width2; // test width for wrapped screen line
+ int width2; // text width for wrapped screen line
oap->motion_type = kMTCharWise;
oap->inclusive = (curwin->w_curswant == MAXCOL);
@@ -3405,6 +3413,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
virtcol -= vim_strsize(get_showbreak_value(curwin));
}
+ int c = utf_ptr2char(get_cursor_pos_ptr());
+ if (dir == FORWARD && virtcol < curwin->w_curswant
+ && (curwin->w_curswant <= (colnr_T)width1)
+ && !vim_isprintc(c) && c > 255) {
+ oneright();
+ }
+
if (virtcol > curwin->w_curswant
&& (curwin->w_curswant < (colnr_T)width1
? (curwin->w_curswant > (colnr_T)width1 / 2)
@@ -4437,11 +4452,7 @@ static void nv_ident(cmdarg_T *cap)
// Start insert mode in terminal buffer
restart_edit = 'i';
- add_map((char_u *)"<buffer> <esc> <Cmd>call jobstop(&channel)<CR>", TERM_FOCUS, true);
- do_cmdline_cmd("autocmd TermClose <buffer> "
- " if !v:event.status |"
- " exec 'bdelete! ' .. expand('<abuf>') |"
- " endif");
+ add_map((char_u *)"<buffer> <esc> <Cmd>bdelete!<CR>", TERM_FOCUS, true);
}
}
@@ -4476,8 +4487,13 @@ bool get_visual_text(cmdarg_T *cap, char_u **pp, size_t *lenp)
*pp = ml_get_pos(&VIsual);
*lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
}
- // Correct the length to include the whole last character.
- *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1);
+ if (**pp == NUL) {
+ *lenp = 0;
+ }
+ if (*lenp > 0) {
+ // Correct the length to include all bytes of the last character.
+ *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1);
+ }
}
reset_VIsual_and_resel();
return true;
@@ -5967,11 +5983,8 @@ static void nv_visual(cmdarg_T *cap)
* was only one -- webb
*/
if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) {
- curwin->w_cursor.lnum +=
- resel_VIsual_line_count * cap->count0 - 1;
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- }
+ curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1;
+ check_cursor();
}
VIsual_mode = resel_VIsual_mode;
if (VIsual_mode == 'v') {
@@ -6050,7 +6063,7 @@ static void n_start_visual_mode(int c)
// Corner case: the 0 position in a tab may change when going into
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
//
- if (c == Ctrl_V && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB) {
+ if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB) {
validate_virtcol();
coladvance(curwin->w_virtcol);
}
@@ -6190,6 +6203,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// start Select mode.
if (cap->arg) {
VIsual_select = true;
+ VIsual_select_reg = 0;
} else {
may_start_select('c');
}
@@ -6317,11 +6331,9 @@ static void nv_g_cmd(cmdarg_T *cap)
break;
case 'M': {
- const char_u *const ptr = get_cursor_line_ptr();
-
oap->motion_type = kMTCharWise;
oap->inclusive = false;
- i = (int)mb_string2cells_len(ptr, STRLEN(ptr));
+ i = linetabsize(get_cursor_line_ptr());
if (cap->count0 > 0 && cap->count0 <= 100) {
coladvance((colnr_T)(i * cap->count0 / 100));
} else {
@@ -6702,11 +6714,26 @@ static void nv_dot(cmdarg_T *cap)
}
}
-/*
- * CTRL-R: undo undo
- */
-static void nv_redo(cmdarg_T *cap)
+// CTRL-R: undo undo or specify register in select mode
+static void nv_redo_or_register(cmdarg_T *cap)
{
+ if (VIsual_select && VIsual_active) {
+ int reg;
+ // Get register name
+ no_mapping++;
+ reg = plain_vgetc();
+ LANGMAP_ADJUST(reg, true);
+ no_mapping--;
+
+ if (reg == '"') {
+ // the unnamed register is 0
+ reg = 0;
+ }
+
+ VIsual_select_reg = valid_yank_reg(reg, true) ? reg : 0;
+ return;
+ }
+
if (!checkclearopq(cap->oap)) {
u_redo((int)cap->count1);
curwin->w_set_curswant = true;
@@ -6955,7 +6982,7 @@ static void adjust_cursor(oparg_T *oap)
if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
&& (!VIsual_active || *p_sel == 'o')
&& !virtual_active()
- && (ve_flags & VE_ONEMORE) == 0) {
+ && (get_ve_flags() & VE_ONEMORE) == 0) {
curwin->w_cursor.col--;
// prevent cursor from moving on the trail byte
mb_adjust_cursor();
@@ -7027,6 +7054,7 @@ static void nv_select(cmdarg_T *cap)
{
if (VIsual_active) {
VIsual_select = true;
+ VIsual_select_reg = 0;
} else if (VIsual_reselect) {
cap->nchar = 'v'; // fake "gv" command
cap->arg = true;
@@ -7161,7 +7189,7 @@ static void nv_esc(cmdarg_T *cap)
void set_cursor_for_append_to_line(void)
{
curwin->w_set_curswant = true;
- if (ve_flags == VE_ALL) {
+ if (get_ve_flags() == VE_ALL) {
const int save_State = State;
// Pretend Insert mode here to allow the cursor on the
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 013a78bdac..83a7c31991 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -267,14 +267,14 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
msg_attr_keep((char *)IObuff, 0, true, false);
}
- /*
- * Set "'[" and "']" marks.
- */
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end.lnum = oap->end.lnum;
- curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
- if (curbuf->b_op_end.col > 0) {
- curbuf->b_op_end.col--;
+ if (!cmdmod.lockmarks) {
+ // Set "'[" and "']" marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end.lnum = oap->end.lnum;
+ curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
+ if (curbuf->b_op_end.col > 0) {
+ curbuf->b_op_end.col--;
+ }
}
changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true);
@@ -694,9 +694,11 @@ void op_reindent(oparg_T *oap, Indenter how)
"%" PRId64 " lines indented ", i),
(int64_t)i);
}
- // set '[ and '] marks
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
+ if (!cmdmod.lockmarks) {
+ // set '[ and '] marks
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
}
/*
@@ -922,8 +924,8 @@ int do_record(int c)
// The recorded text contents.
p = get_recorded();
if (p != NULL) {
- // Remove escaping for CSI and K_SPECIAL in multi-byte chars.
- vim_unescape_csi(p);
+ // Remove escaping for K_SPECIAL in multi-byte chars.
+ vim_unescape_ks(p);
(void)tv_dict_add_str(dict, S_LEN("regcontents"), (const char *)p);
}
@@ -933,7 +935,7 @@ int do_record(int c)
buf[1] = NUL;
(void)tv_dict_add_str(dict, S_LEN("regname"), buf);
- // Get the recorded key hits. K_SPECIAL and CSI will be escaped, this
+ // Get the recorded key hits. K_SPECIAL 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);
@@ -1099,7 +1101,7 @@ int do_execreg(int regname, int colon, int addcr, int silent)
return FAIL;
}
}
- escaped = vim_strsave_escape_csi(reg->y_array[i]);
+ escaped = vim_strsave_escape_ks(reg->y_array[i]);
retval = ins_typebuf(escaped, remap, 0, true, silent);
xfree(escaped);
if (retval == FAIL) {
@@ -1141,7 +1143,7 @@ static void put_reedit_in_typebuf(int silent)
/// Insert register contents "s" into the typeahead buffer, so that it will be
/// executed again.
///
-/// @param esc when true then it is to be taken literally: Escape CSI
+/// @param esc when true then it is to be taken literally: Escape K_SPECIAL
/// characters and no remapping.
/// @param colon add ':' before the line
static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent)
@@ -1156,7 +1158,7 @@ static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent)
char_u *p;
if (esc) {
- p = vim_strsave_escape_csi(s);
+ p = vim_strsave_escape_ks(s);
} else {
p = s;
}
@@ -1435,6 +1437,11 @@ int op_delete(oparg_T *oap)
return FAIL;
}
+ if (VIsual_select && oap->is_VIsual) {
+ // Use the register given with CTRL_R, defaults to zero
+ oap->regname = VIsual_select_reg;
+ }
+
mb_adjust_opend(oap);
/*
@@ -1731,13 +1738,15 @@ int op_delete(oparg_T *oap)
msgmore(curbuf->b_ml.ml_line_count - old_lcount);
setmarks:
- if (oap->motion_type == kMTBlockWise) {
- curbuf->b_op_end.lnum = oap->end.lnum;
- curbuf->b_op_end.col = oap->start.col;
- } else {
- curbuf->b_op_end = oap->start;
+ if (!cmdmod.lockmarks) {
+ if (oap->motion_type == kMTBlockWise) {
+ curbuf->b_op_end.lnum = oap->end.lnum;
+ curbuf->b_op_end.col = oap->start.col;
+ } else {
+ curbuf->b_op_end = oap->start;
+ }
+ curbuf->b_op_start = oap->start;
}
- curbuf->b_op_start = oap->start;
return OK;
}
@@ -2002,9 +2011,11 @@ static int op_replace(oparg_T *oap, int c)
check_cursor();
changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L, true);
- // Set "'[" and "']" marks.
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
+ if (!cmdmod.lockmarks) {
+ // Set "'[" and "']" marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
return OK;
}
@@ -2073,11 +2084,11 @@ void op_tilde(oparg_T *oap)
redraw_curbuf_later(INVERTED);
}
- /*
- * Set '[ and '] marks.
- */
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
+ if (!cmdmod.lockmarks) {
+ // Set '[ and '] marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
if (oap->line_count > p_report) {
smsg(NGETTEXT("%" PRId64 " line changed",
@@ -2196,19 +2207,22 @@ void op_insert(oparg_T *oap, long count1)
// doing block_prep(). When only "block" is used, virtual edit is
// already disabled, but still need it when calling
// coladvance_force().
+ // coladvance_force() uses get_ve_flags() to get the 'virtualedit'
+ // state for the current window. To override that state, we need to
+ // set the window-local value of ve_flags rather than the global value.
if (curwin->w_cursor.coladd > 0) {
- unsigned old_ve_flags = ve_flags;
+ unsigned old_ve_flags = curwin->w_ve_flags;
- ve_flags = VE_ALL;
if (u_save_cursor() == FAIL) {
return;
}
+ curwin->w_ve_flags = VE_ALL;
coladvance_force(oap->op_type == OP_APPEND
? oap->end_vcol + 1 : getviscol());
if (oap->op_type == OP_APPEND) {
--curwin->w_cursor.col;
}
- ve_flags = old_ve_flags;
+ curwin->w_ve_flags = old_ve_flags;
}
// Get the info about the block before entering the text
block_prep(oap, &bd, oap->start.lnum, true);
@@ -2766,14 +2780,14 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
}
}
- /*
- * Set "'[" and "']" marks.
- */
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
- if (yank_type == kMTLineWise) {
- curbuf->b_op_start.col = 0;
- curbuf->b_op_end.col = MAXCOL;
+ if (!cmdmod.lockmarks) {
+ // Set "'[" and "']" marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ if (yank_type == kMTLineWise) {
+ curbuf->b_op_start.col = 0;
+ curbuf->b_op_end.col = MAXCOL;
+ }
}
return;
@@ -2801,7 +2815,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx,
if (exclude_trailing_space) {
int s = bd->textlen + bd->endspaces;
- while (ascii_iswhite(*(bd->textstart + s - 1)) && s > 0) {
+ while (s > 0 && ascii_iswhite(*(bd->textstart + s - 1))) {
s = s - utf_head_off(bd->textstart, bd->textstart + s - 1) - 1;
pnew--;
}
@@ -2906,6 +2920,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
char_u *insert_string = NULL;
bool allocated = false;
long cnt;
+ const pos_T orig_start = curbuf->b_op_start;
+ const pos_T orig_end = curbuf->b_op_end;
+ unsigned int cur_ve_flags = get_ve_flags();
if (flags & PUT_FIXINDENT) {
orig_indent = get_indent();
@@ -2976,7 +2993,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL);
}
- bool ve_allows = (ve_flags == VE_ALL || ve_flags == VE_ONEMORE);
+ bool ve_allows = (cur_ve_flags == VE_ALL || cur_ve_flags == VE_ONEMORE);
bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum
&& one_past_line;
if (ve_allows || !(eol || eof)) {
@@ -3152,13 +3169,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
yanklen = (int)STRLEN(y_array[0]);
- if (ve_flags == VE_ALL && y_type == kMTCharWise) {
+ if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
- /* Don't need to insert spaces when "p" on the last position of a
- * tab or "P" on the first position. */
int viscol = getviscol();
+ long ts = curbuf->b_p_ts;
+ // Don't need to insert spaces when "p" on the last position of a
+ // tab or "P" on the first position.
if (dir == FORWARD
- ? tabstop_padding(viscol, curbuf->b_p_ts, curbuf->b_p_vts_array) != 1
+ ? tabstop_padding(viscol, ts, curbuf->b_p_vts_array) != 1
: curwin->w_cursor.coladd > 0) {
coladvance_force(viscol);
} else {
@@ -3180,7 +3198,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
colnr_T endcol2 = 0;
if (dir == FORWARD && c != NUL) {
- if (ve_flags == VE_ALL) {
+ if (cur_ve_flags == VE_ALL) {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
} else {
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
@@ -3194,9 +3212,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
col += curwin->w_cursor.coladd;
- if (ve_flags == VE_ALL
- && (curwin->w_cursor.coladd > 0
- || endcol2 == curwin->w_cursor.col)) {
+ if (cur_ve_flags == VE_ALL
+ && (curwin->w_cursor.coladd > 0 || endcol2 == curwin->w_cursor.col)) {
if (dir == FORWARD && c == NUL) {
col++;
}
@@ -3609,6 +3626,10 @@ error:
curwin->w_set_curswant = TRUE;
end:
+ if (cmdmod.lockmarks) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
+ }
if (allocated) {
xfree(insert_string);
}
@@ -3628,14 +3649,16 @@ end:
*/
void adjust_cursor_eol(void)
{
+ unsigned int cur_ve_flags = get_ve_flags();
+
if (curwin->w_cursor.col > 0
&& gchar_cursor() == NUL
- && (ve_flags & VE_ONEMORE) == 0
+ && (cur_ve_flags & VE_ONEMORE) == 0
&& !(restart_edit || (State & INSERT))) {
// Put the cursor on the last character in the line.
dec_cursor();
- if (ve_flags == VE_ALL) {
+ if (cur_ve_flags == VE_ALL) {
colnr_T scol, ecol;
// Coladd is set to the width of the last character.
@@ -3953,7 +3976,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
// and setup the array of space strings lengths
for (t = 0; t < (linenr_T)count; t++) {
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
- if (t == 0 && setmark) {
+ if (t == 0 && setmark && !cmdmod.lockmarks) {
// Set the '[ mark.
curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum;
curwin->w_buffer->b_op_start.col = (colnr_T)STRLEN(curr);
@@ -4074,7 +4097,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
ml_replace(curwin->w_cursor.lnum, newp, false);
- if (setmark) {
+ if (setmark && !cmdmod.lockmarks) {
// Set the '] mark.
curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum;
curwin->w_buffer->b_op_end.col = sumsize;
@@ -4212,8 +4235,10 @@ static void op_format(oparg_T *oap, int keep_cursor)
redraw_curbuf_later(INVERTED);
}
- // Set '[ mark at the start of the formatted area
- curbuf->b_op_start = oap->start;
+ if (!cmdmod.lockmarks) {
+ // Set '[ mark at the start of the formatted area
+ curbuf->b_op_start = oap->start;
+ }
// For "gw" remember the cursor position and put it back below (adjusted
// for joined and split lines).
@@ -4235,8 +4260,10 @@ static void op_format(oparg_T *oap, int keep_cursor)
old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
msgmore(old_line_count);
- // put '] mark on the end of the formatted area
- curbuf->b_op_end = curwin->w_cursor;
+ if (!cmdmod.lockmarks) {
+ // put '] mark on the end of the formatted area
+ curbuf->b_op_end = curwin->w_cursor;
+ }
if (keep_cursor) {
curwin->w_cursor = saved_cursor;
@@ -4834,7 +4861,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
// Set '[ mark if something changed. Keep the last end
// position from do_addsub().
- if (change_cnt > 0) {
+ if (change_cnt > 0 && !cmdmod.lockmarks) {
curbuf->b_op_start = startpos;
}
@@ -5188,11 +5215,13 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
}
- // set the '[ and '] marks
- curbuf->b_op_start = startpos;
- curbuf->b_op_end = endpos;
- if (curbuf->b_op_end.col > 0) {
- curbuf->b_op_end.col--;
+ if (!cmdmod.lockmarks) {
+ // set the '[ and '] marks
+ curbuf->b_op_start = startpos;
+ curbuf->b_op_end = endpos;
+ if (curbuf->b_op_end.col > 0) {
+ curbuf->b_op_end.col--;
+ }
}
theend:
@@ -6005,6 +6034,8 @@ static void op_function(const oparg_T *oap)
{
const TriState save_virtual_op = virtual_op;
const bool save_finish_op = finish_op;
+ const pos_T orig_start = curbuf->b_op_start;
+ const pos_T orig_end = curbuf->b_op_end;
if (*p_opfunc == NUL) {
emsg(_("E774: 'operatorfunc' is empty"));
@@ -6038,6 +6069,10 @@ static void op_function(const oparg_T *oap)
virtual_op = save_virtual_op;
finish_op = save_finish_op;
+ if (cmdmod.lockmarks) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
+ }
}
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 659965b64c..a4a6423ac7 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -281,7 +281,7 @@ typedef struct vimoption {
# include "options.generated.h"
#endif
-#define PARAM_COUNT ARRAY_SIZE(options)
+#define OPTION_COUNT ARRAY_SIZE(options)
static char *(p_ambw_values[]) = { "single", "double", NULL };
static char *(p_bg_values[]) = { "light", "dark", NULL };
@@ -931,6 +931,21 @@ void set_title_defaults(void)
}
}
+void ex_set(exarg_T *eap)
+{
+ int flags = 0;
+
+ if (eap->cmdidx == CMD_setlocal) {
+ flags = OPT_LOCAL;
+ } else if (eap->cmdidx == CMD_setglobal) {
+ flags = OPT_GLOBAL;
+ }
+ if (eap->forceit) {
+ flags |= OPT_ONECOLUMN;
+ }
+ (void)do_set(eap->arg, flags);
+}
+
/// Parse 'arg' for option settings.
///
/// 'arg' may be IObuff, but only when no errors can be present and option
@@ -1993,9 +2008,9 @@ static void didset_options2(void)
// Parse default for 'wildmode'.
check_opt_wim();
xfree(curbuf->b_p_vsts_array);
- tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array);
+ (void)tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array);
xfree(curbuf->b_p_vts_array);
- tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array);
+ (void)tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array);
}
/// Check for string options that are NULL (normally only termcap options).
@@ -3084,14 +3099,27 @@ ambw_end:
if (foldmethodIsIndent(curwin)) {
foldUpdateAll(curwin);
}
- } else if (varp == &p_ve) { // 'virtualedit'
- if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK) {
- errmsg = e_invarg;
- } else if (STRCMP(p_ve, oldval) != 0) {
- // Recompute cursor position in case the new 've' setting
- // changes something.
- validate_virtcol();
- coladvance(curwin->w_virtcol);
+ } else if (gvarp == &p_ve) { // 'virtualedit'
+ char_u *ve = p_ve;
+ unsigned int *flags = &ve_flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ ve = curwin->w_p_ve;
+ flags = &curwin->w_ve_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *ve == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else {
+ if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) {
+ errmsg = e_invarg;
+ } else if (STRCMP(p_ve, oldval) != 0) {
+ // Recompute cursor position in case the new 've' setting
+ // changes something.
+ validate_virtcol();
+ coladvance(curwin->w_virtcol);
+ }
}
} else if (varp == &p_csqf) {
if (p_csqf != NULL) {
@@ -5184,7 +5212,7 @@ static void showoptions(int all, int opt_flags)
#define INC 20
#define GAP 3
- vimoption_T **items = xmalloc(sizeof(vimoption_T *) * PARAM_COUNT);
+ vimoption_T **items = xmalloc(sizeof(vimoption_T *) * OPTION_COUNT);
// Highlight title
if (opt_flags & OPT_GLOBAL) {
@@ -5198,6 +5226,7 @@ static void showoptions(int all, int opt_flags)
// Do the loop two times:
// 1. display the short items
// 2. display the long items (only strings and numbers)
+ // When "opt_flags" has OPT_ONECOLUMN do everything in run 2.
for (run = 1; run <= 2 && !got_int; run++) {
// collect the items in items[]
item_count = 0;
@@ -5208,7 +5237,7 @@ static void showoptions(int all, int opt_flags)
}
varp = NULL;
- if (opt_flags != 0) {
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) {
if (p->indir != PV_NONE) {
varp = get_varp_scope(p, opt_flags);
}
@@ -5217,8 +5246,10 @@ static void showoptions(int all, int opt_flags)
}
if (varp != NULL
&& (all == 1 || (all == 0 && !optval_default(p, varp)))) {
- if (p->flags & P_BOOL) {
- len = 1; // a toggle option fits always
+ if (opt_flags & OPT_ONECOLUMN) {
+ len = Columns;
+ } else if (p->flags & P_BOOL) {
+ len = 1; // a toggle option fits always
} else {
option_value2string(p, opt_flags);
len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1;
@@ -5748,6 +5779,10 @@ void unset_global_local_option(char *name, void *from)
set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true);
redraw_later((win_T *)from, NOT_VALID);
break;
+ case PV_VE:
+ clear_string_option(&((win_T *)from)->w_p_ve);
+ ((win_T *)from)->w_ve_flags = 0;
+ break;
}
}
@@ -5814,6 +5849,8 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
return (char_u *)&(curwin->w_p_fcs);
case PV_LCS:
return (char_u *)&(curwin->w_p_lcs);
+ case PV_VE:
+ return (char_u *)&(curwin->w_p_ve);
}
return NULL; // "cannot happen"
}
@@ -5908,6 +5945,9 @@ static char_u *get_varp(vimoption_T *p)
case PV_LCS:
return *curwin->w_p_lcs != NUL
? (char_u *)&(curwin->w_p_lcs) : p->var;
+ case PV_VE:
+ return *curwin->w_p_ve != NUL
+ ? (char_u *)&curwin->w_p_ve : p->var;
case PV_ARAB:
return (char_u *)&(curwin->w_p_arab);
@@ -6148,6 +6188,8 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_list = from->wo_list;
to->wo_nu = from->wo_nu;
to->wo_rnu = from->wo_rnu;
+ to->wo_ve = vim_strsave(from->wo_ve);
+ to->wo_ve_flags = from->wo_ve_flags;
to->wo_nuw = from->wo_nuw;
to->wo_rl = from->wo_rl;
to->wo_rlc = vim_strsave(from->wo_rlc);
@@ -6224,6 +6266,7 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_winhl);
check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_lcs);
+ check_string_option(&wop->wo_ve);
}
/// Free the allocated memory inside a winopt_T.
@@ -6248,6 +6291,7 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_winhl);
clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_lcs);
+ clear_string_option(&wop->wo_ve);
}
void didset_window_options(win_T *wp)
@@ -6368,7 +6412,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_sts_nopaste = p_sts_nopaste;
buf->b_p_vsts = vim_strsave(p_vsts);
if (p_vsts && p_vsts != empty_option) {
- tabstop_set(p_vsts, &buf->b_p_vsts_array);
+ (void)tabstop_set(p_vsts, &buf->b_p_vsts_array);
} else {
buf->b_p_vsts_array = 0;
}
@@ -6448,7 +6492,7 @@ void buf_copy_options(buf_T *buf, int flags)
if (dont_do_help) {
buf->b_p_isk = save_p_isk;
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
- tabstop_set(p_vts, &buf->b_p_vts_array);
+ (void)tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
}
@@ -6458,7 +6502,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_ts = p_ts;
buf->b_p_vts = vim_strsave(p_vts);
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
- tabstop_set(p_vts, &buf->b_p_vts_array);
+ (void)tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
}
@@ -7151,7 +7195,7 @@ static void paste_option_changed(void)
xfree(buf->b_p_vsts_array);
}
if (buf->b_p_vsts && buf->b_p_vsts != empty_option) {
- tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
+ (void)tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
} else {
buf->b_p_vsts_array = 0;
}
@@ -7469,6 +7513,7 @@ int check_ff_value(char_u *p)
// Set the integer values corresponding to the string setting of 'vartabstop'.
// "array" will be set, caller must free it if needed.
+// Return false for an error.
bool tabstop_set(char_u *var, long **array)
{
long valcount = 1;
@@ -7488,7 +7533,7 @@ bool tabstop_set(char_u *var, long **array)
if (cp != end) {
emsg(_(e_positive));
} else {
- emsg(_(e_invarg));
+ semsg(_(e_invarg2), cp);
}
return false;
}
@@ -7501,7 +7546,7 @@ bool tabstop_set(char_u *var, long **array)
valcount++;
continue;
}
- emsg(_(e_invarg));
+ semsg(_(e_invarg2), var);
return false;
}
@@ -7510,7 +7555,15 @@ bool tabstop_set(char_u *var, long **array)
t = 1;
for (cp = var; *cp != NUL;) {
- (*array)[t++] = atoi((char *)cp);
+ int n = atoi((char *)cp);
+
+ // Catch negative values, overflow and ridiculous big values.
+ if (n < 0 || n > 9999) {
+ semsg(_(e_invarg2), cp);
+ XFREE_CLEAR(*array);
+ return false;
+ }
+ (*array)[t++] = n;
while (*cp != NUL && *cp != ',') {
cp++;
}
@@ -7815,6 +7868,12 @@ unsigned int get_bkc_value(buf_T *buf)
return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
}
+/// Get the local or global value of the 'virtualedit' flags.
+unsigned int get_ve_flags(void)
+{
+ return (curwin->w_ve_flags ? curwin->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU);
+}
+
/// Get the local or global value of 'showbreak'.
///
/// @param win If not NULL, the window to get the local option from; global
diff --git a/src/nvim/option.h b/src/nvim/option.h
index f7dbaafeec..9321dd5454 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -13,16 +13,16 @@
/// When OPT_GLOBAL and OPT_LOCAL are both missing, set both local and global
/// values, get local value.
typedef enum {
- OPT_FREE = 1, ///< Free old value if it was allocated.
- OPT_GLOBAL = 2, ///< Use global value.
- OPT_LOCAL = 4, ///< Use local value.
- OPT_MODELINE = 8, ///< Option in modeline.
- OPT_WINONLY = 16, ///< Only set window-local options.
- OPT_NOWIN = 32, ///< Don’t set window-local options.
- OPT_ONECOLUMN = 64, ///< list options one per line
- OPT_NO_REDRAW = 128, ///< ignore redraw flags on option
- OPT_SKIPRTP = 256, ///< "skiprtp" in 'sessionoptions'
- OPT_CLEAR = 512, ///< Clear local value of an option.
+ OPT_FREE = 0x01, ///< Free old value if it was allocated.
+ OPT_GLOBAL = 0x02, ///< Use global value.
+ OPT_LOCAL = 0x04, ///< Use local value.
+ OPT_MODELINE = 0x08, ///< Option in modeline.
+ OPT_WINONLY = 0x10, ///< Only set window-local options.
+ OPT_NOWIN = 0x20, ///< Don’t set window-local options.
+ OPT_ONECOLUMN = 0x40, ///< list options one per line
+ OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option
+ OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions'
+ OPT_CLEAR = 0x200, ///< Clear local value of an option.
} OptionFlags;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 09c3bf3800..5d6aca9574 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -705,12 +705,14 @@ EXTERN int p_vb; ///< 'visualbell'
EXTERN char_u *p_ve; ///< 'virtualedit'
EXTERN unsigned ve_flags;
#ifdef IN_OPTION_C
-static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", NULL };
+static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
#endif
-#define VE_BLOCK 5 // includes "all"
-#define VE_INSERT 6 // includes "all"
-#define VE_ALL 4
-#define VE_ONEMORE 8
+#define VE_BLOCK 5U // includes "all"
+#define VE_INSERT 6U // includes "all"
+#define VE_ALL 4U
+#define VE_ONEMORE 8U
+#define VE_NONE 16U // "none"
+#define VE_NONEU 32U // "NONE"
EXTERN long p_verbose; // 'verbose'
#ifdef IN_OPTION_C
char_u *p_vfile = (char_u *)""; // used before options are initialized
@@ -869,6 +871,7 @@ enum {
WV_LBR,
WV_NU,
WV_RNU,
+ WV_VE,
WV_NUW,
WV_PVW,
WV_RL,
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 5133fe7ac8..aea2179a61 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2736,7 +2736,7 @@ return {
{
full_name='virtualedit', abbreviation='ve',
short_desc=N_("when to use virtual editing"),
- type='string', list='onecomma', scope={'global'},
+ type='string', list='onecomma', scope={'global', 'window'},
deny_duplicates=true,
redraw={'curswant'},
varname='p_ve',
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 3790eba212..54cfaee80a 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -234,9 +234,9 @@ size_t input_enqueue(String keys)
while (rbuffer_space(input_buffer) >= 19 && ptr < end) {
// A "<x>" form occupies at least 1 characters, and produces up
// to 19 characters (1 + 5 * 3 for the char and 3 for a modifier).
- // In the case of K_SPECIAL(0x80) or CSI(0x9B), 3 bytes are escaped and
- // needed, but since the keys are UTF-8, so the first byte cannot be
- // K_SPECIAL(0x80) or CSI(0x9B).
+ // In the case of K_SPECIAL(0x80), 3 bytes are escaped and needed,
+ // but since the keys are UTF-8, so the first byte cannot be
+ // K_SPECIAL(0x80).
uint8_t buf[19] = { 0 };
unsigned int new_size
= trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true,
@@ -263,12 +263,8 @@ size_t input_enqueue(String keys)
continue;
}
- // copy the character, escaping CSI and K_SPECIAL
- if ((uint8_t)*ptr == CSI) {
- rbuffer_write(input_buffer, (char *)&(uint8_t){ K_SPECIAL }, 1);
- rbuffer_write(input_buffer, (char *)&(uint8_t){ KS_EXTRA }, 1);
- rbuffer_write(input_buffer, (char *)&(uint8_t){ KE_CSI }, 1);
- } else if ((uint8_t)*ptr == K_SPECIAL) {
+ // copy the character, escaping K_SPECIAL
+ if ((uint8_t)(*ptr) == K_SPECIAL) {
rbuffer_write(input_buffer, (char *)&(uint8_t){ K_SPECIAL }, 1);
rbuffer_write(input_buffer, (char *)&(uint8_t){ KS_SPECIAL }, 1);
rbuffer_write(input_buffer, (char *)&(uint8_t){ KE_FILLER }, 1);
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 674d67e21a..39e276e0a5 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1682,6 +1682,10 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
char_u *file_name;
char_u *tofree = NULL;
+ if (len == 0) {
+ return NULL;
+ }
+
if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
tofree = (char_u *)eval_includeexpr((char *)ptr, len);
if (tofree != NULL) {
@@ -1743,14 +1747,32 @@ int path_is_url(const char *p)
return 0;
}
-/// Check if "fname" starts with "name://". Return URL_SLASH if it does.
+/// Check if "fname" starts with "name://" or "name:\\".
///
/// @param fname is the filename to test
-/// @return URL_BACKSLASH for "name:\\", zero otherwise.
+/// @return URL_SLASH for "name://", URL_BACKSLASH for "name:\\", zero otherwise.
int path_with_url(const char *fname)
{
const char *p;
- for (p = fname; isalpha(*p); p++) {}
+
+ // We accept alphabetic characters and a dash in scheme part.
+ // RFC 3986 allows for more, but it increases the risk of matching
+ // non-URL text.
+
+ // first character must be alpha
+ if (!isalpha(*fname)) {
+ return 0;
+ }
+
+ // check body: alpha or dash
+ for (p = fname; (isalpha(*p) || (*p == '-')); p++) {}
+
+ // check last char is not a dash
+ if (p[-1] == '-') {
+ return 0;
+ }
+
+ // "://" or ":\\" must follow
return path_is_url(p);
}
diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po
index 5dda7c59f5..9633bec9f2 100644
--- a/src/nvim/po/ja.euc-jp.po
+++ b/src/nvim/po/ja.euc-jp.po
@@ -1644,15 +1644,6 @@ msgstr " [w]"
msgid " written"
msgstr " "
-msgid "E205: Patchmode: can't save original file"
-msgstr "E205: patchmode: ܥե¸Ǥޤ"
-
-msgid "E206: patchmode: can't touch empty original file"
-msgstr "E206: patchmode: θܥեtouchǤޤ"
-
-msgid "E207: Can't delete backup file"
-msgstr "E207: Хååץեäޤ"
-
msgid ""
"\n"
"WARNING: Original file may be lost or damaged\n"
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index a169bd3589..c363c00fa6 100644
--- a/src/nvim/po/ja.po
+++ b/src/nvim/po/ja.po
@@ -1644,15 +1644,6 @@ msgstr " [w]"
msgid " written"
msgstr " 書込み"
-msgid "E205: Patchmode: can't save original file"
-msgstr "E205: patchmode: 原本ファイルを保存できません"
-
-msgid "E206: patchmode: can't touch empty original file"
-msgstr "E206: patchmode: 空の原本ファイルをtouchできません"
-
-msgid "E207: Can't delete backup file"
-msgstr "E207: バックアップファイルを消せません"
-
msgid ""
"\n"
"WARNING: Original file may be lost or damaged\n"
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 45e580dbee..c8508179a1 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -3232,7 +3232,7 @@ typedef struct {
// The current match-position is remembered with these variables:
linenr_T lnum; ///< line number, relative to first line
char_u *line; ///< start of current line
- char_u *input; ///< current input, points into "regline"
+ char_u *input; ///< current input, points into "line"
int need_clear_subexpr; ///< subexpressions still need to be cleared
int need_clear_zsubexpr; ///< extmatch subexpressions still need to be
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index cafffc0319..133858f113 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -6071,8 +6071,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_MARK_GT:
case NFA_MARK_LT:
{
+ size_t col = rex.input - rex.line;
pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false);
+ // Line may have been freed, get it again.
+ if (REG_MULTI) {
+ rex.line = reg_getline(rex.lnum);
+ rex.input = rex.line + col;
+ }
+
// Compare the mark position to the match position, if the mark
// exists and mark is set in reg_buf.
if (pos != NULL && pos->lnum > 0) {
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index b1ca8c5805..af023d6785 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1215,19 +1215,40 @@ static void win_update(win_T *wp, Providers *providers)
*/
if (VIsual_mode == Ctrl_V) {
colnr_T fromc, toc;
- int save_ve_flags = ve_flags;
+ unsigned int save_ve_flags = curwin->w_ve_flags;
if (curwin->w_p_lbr) {
- ve_flags = VE_ALL;
+ curwin->w_ve_flags = VE_ALL;
}
getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
- ve_flags = save_ve_flags;
toc++;
+ curwin->w_ve_flags = save_ve_flags;
// Highlight to the end of the line, unless 'virtualedit' has
// "block".
- if (curwin->w_curswant == MAXCOL && !(ve_flags & VE_BLOCK)) {
- toc = MAXCOL;
+ if (curwin->w_curswant == MAXCOL) {
+ if (get_ve_flags() & VE_BLOCK) {
+ pos_T pos;
+ int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
+
+ // Need to find the longest line.
+ toc = 0;
+ pos.coladd = 0;
+ for (pos.lnum = curwin->w_cursor.lnum;
+ cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum;
+ pos.lnum += cursor_above ? 1 : -1) {
+ colnr_T t;
+
+ pos.col = STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
+ getvvcol(wp, &pos, NULL, NULL, &t);
+ if (toc < t) {
+ toc = t;
+ }
+ }
+ toc++;
+ } else {
+ toc = MAXCOL;
+ }
}
if (fromc != wp->w_old_cursor_fcol
@@ -3720,41 +3741,46 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
tab_len += n_extra - tab_len;
}
- // if n_extra > 0, it gives the number of chars
+ // If n_extra > 0, it gives the number of chars
// to use for a tab, else we need to calculate the width
- // for a tab
+ // for a tab.
int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2));
+ if (wp->w_p_lcs_chars.tab3) {
+ len += utf_char2len(wp->w_p_lcs_chars.tab3);
+ }
if (n_extra > 0) {
len += n_extra - tab_len;
}
c = wp->w_p_lcs_chars.tab1;
p = xmalloc(len + 1);
- memset(p, ' ', len);
- p[len] = NUL;
- xfree(p_extra_free);
- p_extra_free = p;
- for (i = 0; i < tab_len; i++) {
- if (*p == NUL) {
- tab_len = i;
- break;
- }
- int lcs = wp->w_p_lcs_chars.tab2;
+ if (p == NULL) {
+ n_extra = 0;
+ } else {
+ memset(p, ' ', len);
+ p[len] = NUL;
+ xfree(p_extra_free);
+ p_extra_free = p;
+ for (i = 0; i < tab_len; i++) {
+ if (*p == NUL) {
+ tab_len = i;
+ break;
+ }
+ int lcs = wp->w_p_lcs_chars.tab2;
- // if tab3 is given, need to change the char
- // for tab
- if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
- lcs = wp->w_p_lcs_chars.tab3;
+ // if tab3 is given, use it for the last char
+ if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
+ lcs = wp->w_p_lcs_chars.tab3;
+ }
+ p += utf_char2bytes(lcs, p);
+ n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
}
- utf_char2bytes(lcs, p);
- p += utf_char2len(lcs);
- n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
- }
- p_extra = p_extra_free;
+ p_extra = p_extra_free;
- // n_extra will be increased by FIX_FOX_BOGUSCOLS
- // macro below, so need to adjust for that here
- if (vcol_off > 0) {
- n_extra -= vcol_off;
+ // n_extra will be increased by FIX_FOX_BOGUSCOLS
+ // macro below, so need to adjust for that here
+ if (vcol_off > 0) {
+ n_extra -= vcol_off;
+ }
}
}
@@ -4130,7 +4156,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (((wp->w_p_cuc
&& (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off
&& (int)wp->w_virtcol <
- grid->Columns * (row - startrow + 1) + v
+ (long)grid->Columns * (row - startrow + 1) + v
&& lnum != wp->w_cursor.lnum)
|| draw_color_col || line_attr_lowprio || line_attr
|| diff_hlf != (hlf_T)0 || has_virttext)) {
@@ -4232,6 +4258,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// Show "extends" character from 'listchars' if beyond the line end and
// 'list' is set.
if (wp->w_p_lcs_chars.ext != NUL
+ && draw_state == WL_LINE
&& wp->w_p_list
&& !wp->w_p_wrap
&& filler_todo <= 0
@@ -4427,7 +4454,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
*/
if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
&& foldinfo.fi_lines == 0
- && (*ptr != NUL
+ && (draw_state != WL_LINE
+ || *ptr != NUL
|| filler_todo > 0
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
&& p_extra != at_end_str)
@@ -6762,7 +6790,7 @@ void grid_clear_line(ScreenGrid *grid, unsigned off, int width, bool valid)
void grid_invalidate(ScreenGrid *grid)
{
- (void)memset(grid->attrs, -1, grid->Rows * grid->Columns * sizeof(sattr_T));
+ (void)memset(grid->attrs, -1, sizeof(sattr_T) * grid->Rows * grid->Columns);
}
bool grid_invalid_row(ScreenGrid *grid, int row)
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 9429a06e92..1296d410f6 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -4082,7 +4082,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// char, e.g., "thes," -> "these".
p = fword + sp->ts_fidx;
MB_PTR_BACK(fword, p);
- if (!spell_iswordp(p, curwin)) {
+ if (!spell_iswordp(p, curwin) && *preword != NUL) {
p = preword + STRLEN(preword);
MB_PTR_BACK(preword, p);
if (spell_iswordp(p, curwin)) {
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 68bc76660d..9e4c9b2bad 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -12,6 +12,7 @@
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/main.h"
+#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/os/input.h"
#include "nvim/state.h"
@@ -107,15 +108,17 @@ void state_handle_k_event(void)
/// Return true if in the current mode we need to use virtual.
bool virtual_active(void)
{
+ unsigned int cur_ve_flags = get_ve_flags();
+
// While an operator is being executed we return "virtual_op", because
// VIsual_active has already been reset, thus we can't check for "block"
// being used.
if (virtual_op != kNone) {
return virtual_op;
}
- return ve_flags == VE_ALL
- || ((ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
- || ((ve_flags & VE_INSERT) && (State & INSERT));
+ return cur_ve_flags == VE_ALL
+ || ((cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
+ || ((cur_ve_flags & VE_INSERT) && (State & INSERT));
}
/// VISUAL, SELECTMODE and OP_PENDING State are never set, they are equal to
@@ -180,7 +183,7 @@ char *get_mode(void)
buf[1] = 'x';
}
}
- } else if (State & CMDLINE) {
+ } else if ((State & CMDLINE) || exmode_active) {
buf[0] = 'c';
if (exmode_active) {
buf[1] = 'v';
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index e2a8108c45..291138ef23 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -1001,22 +1001,20 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
- str_arg);
}
if (fmt_spec == 'S') {
- if (min_field_width != 0) {
- min_field_width += (strlen(str_arg)
- - mb_string2cells((char_u *)str_arg));
- }
- if (precision) {
- char_u *p1;
- size_t i = 0;
-
- for (p1 = (char_u *)str_arg; *p1;
- p1 += utfc_ptr2len(p1)) {
- i += (size_t)utf_ptr2cells(p1);
- if (i > precision) {
- break;
- }
+ char_u *p1;
+ size_t i;
+
+ for (i = 0, p1 = (char_u *)str_arg; *p1; p1 += utfc_ptr2len(p1)) {
+ size_t cell = (size_t)utf_ptr2cells(p1);
+ if (precision_specified && i + cell > precision) {
+ break;
}
- str_arg_l = (size_t)(p1 - (char_u *)str_arg);
+ i += cell;
+ }
+
+ str_arg_l = (size_t)(p1 - (char_u *)str_arg);
+ if (min_field_width != 0) {
+ min_field_width += str_arg_l - i;
}
}
break;
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 70a5c7aa08..a2d855244c 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -1299,7 +1299,7 @@ static bool send_mouse_event(Terminal *term, int c)
}
end:
- ins_char_typebuf(c);
+ ins_char_typebuf(c, mod_mask);
return true;
}
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 14bab33a2f..ab26dddbe0 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -12,9 +12,9 @@ endfunc
" Command to check for the absence of a feature.
command -nargs=1 CheckNotFeature call CheckNotFeature(<f-args>)
func CheckNotFeature(name)
- if !has(a:name, 1)
- throw 'Checking for non-existent feature ' .. a:name
- endif
+ " if !has(a:name, 1)
+ " throw 'Checking for non-existent feature ' .. a:name
+ " endif
if has(a:name)
throw 'Skipped: ' .. a:name .. ' feature present'
endif
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index cc767a9bcf..c0ac4393c4 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -27,6 +27,7 @@ source test_join.vim
source test_jumps.vim
source test_fileformat.vim
source test_filetype.vim
+source test_filetype_lua.vim
source test_lambda.vim
source test_menu.vim
source test_messages.vim
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 45285b69a1..231ab2acf1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -2380,6 +2380,40 @@ func Test_autocmd_was_using_freed_memory()
pclose
endfunc
+func Test_BufWrite_lockmarks()
+ edit! Xtest
+ call setline(1, ['a', 'b', 'c', 'd'])
+
+ " :lockmarks preserves the marks
+ call SetChangeMarks(2, 3)
+ lockmarks write
+ call assert_equal([2, 3], [line("'["), line("']")])
+
+ " *WritePre autocmds get the correct line range, but lockmarks preserves the
+ " original values for the user
+ augroup lockmarks
+ au!
+ au BufWritePre,FilterWritePre * call assert_equal([1, 4], [line("'["), line("']")])
+ au FileWritePre * call assert_equal([3, 4], [line("'["), line("']")])
+ augroup END
+
+ lockmarks write
+ call assert_equal([2, 3], [line("'["), line("']")])
+
+ if executable('cat')
+ lockmarks %!cat
+ call assert_equal([2, 3], [line("'["), line("']")])
+ endif
+
+ lockmarks 3,4write Xtest2
+ call assert_equal([2, 3], [line("'["), line("']")])
+
+ au! lockmarks
+ augroup! lockmarks
+ call delete('Xtest')
+ call delete('Xtest2')
+endfunc
+
" FileChangedShell tested in test_filechanged.vim
func LogACmd()
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 40111fdf06..a31cdbb49a 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -1,5 +1,7 @@
" Tests for Vim buffer
+source check.vim
+
func Test_buffer_error()
new foo1
new foo2
@@ -30,4 +32,33 @@ func Test_balt()
call assert_equal('OtherBuffer', bufname())
endfunc
+" Test for buffer match URL(scheme) check
+" scheme is alpha and inner hyphen only.
+func Test_buffer_scheme()
+ CheckMSWindows
+
+ set noshellslash
+ %bwipe!
+ let bufnames = [
+ \ #{id: 'b0', name: 'test://xyz/foo/b0' , match: 1},
+ \ #{id: 'b1', name: 'test+abc://xyz/foo/b1', match: 0},
+ \ #{id: 'b2', name: 'test_abc://xyz/foo/b2', match: 0},
+ \ #{id: 'b3', name: 'test-abc://xyz/foo/b3', match: 1},
+ \ #{id: 'b4', name: '-test://xyz/foo/b4' , match: 0},
+ \ #{id: 'b5', name: 'test-://xyz/foo/b5' , match: 0},
+ \]
+ for buf in bufnames
+ new `=buf.name`
+ if buf.match
+ call assert_equal(buf.name, getbufinfo(buf.id)[0].name)
+ else
+ " slashes will have become backslashes
+ call assert_notequal(buf.name, getbufinfo(buf.id)[0].name)
+ endif
+ bwipe
+ endfor
+
+ set shellslash&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 1672b0e840..ff4cbe544c 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -500,8 +500,16 @@ func Test_fullcommand()
for [in, want] in items(tests)
call assert_equal(want, fullcommand(in))
endfor
+ call assert_equal('', fullcommand(v:_null_string))
call assert_equal('syntax', 'syn'->fullcommand())
+
+ command -buffer BufferLocalCommand :
+ command GlobalCommand :
+ call assert_equal('GlobalCommand', fullcommand('GlobalCom'))
+ call assert_equal('BufferLocalCommand', fullcommand('BufferL'))
+ delcommand BufferLocalCommand
+ delcommand GlobalCommand
endfunc
func Test_shellcmd_completion()
diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim
index aaa2301bca..c0c572ce65 100644
--- a/src/nvim/testdir/test_compiler.vim
+++ b/src/nvim/testdir/test_compiler.vim
@@ -19,6 +19,9 @@ func Test_compiler()
call assert_equal('perl', b:current_compiler)
call assert_fails('let g:current_compiler', 'E121:')
+ let verbose_efm = execute('verbose set efm')
+ call assert_match('Last set from .*[/\\]compiler[/\\]perl.vim ', verbose_efm)
+
call setline(1, ['#!/usr/bin/perl -w', 'use strict;', 'my $foo=1'])
w!
call feedkeys(":make\<CR>\<CR>", 'tx')
diff --git a/src/nvim/testdir/test_conceal.vim b/src/nvim/testdir/test_conceal.vim
index 1306dbe5cf..bffc2f49d3 100644
--- a/src/nvim/testdir/test_conceal.vim
+++ b/src/nvim/testdir/test_conceal.vim
@@ -4,10 +4,10 @@ source check.vim
CheckFeature conceal
source screendump.vim
-" CheckScreendump
func Test_conceal_two_windows()
CheckScreendump
+
let code =<< trim [CODE]
let lines = ["one one one one one", "two |hidden| here", "three |hidden| three"]
call setline(1, lines)
@@ -111,6 +111,7 @@ endfunc
func Test_conceal_with_cursorline()
CheckScreendump
+
" Opens a help window, where 'conceal' is set, switches to the other window
" where 'cursorline' needs to be updated when the cursor moves.
let code =<< trim [CODE]
@@ -139,6 +140,7 @@ endfunc
func Test_conceal_resize_term()
CheckScreendump
+
let code =<< trim [CODE]
call setline(1, '`one` `two` `three` `four` `five`, the backticks should be concealed')
setl cocu=n cole=3
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 3a0c615cf6..482d39056f 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -1146,6 +1146,35 @@ func Test_diff_followwrap()
bwipe!
endfunc
+func Test_diff_maintains_change_mark()
+ func DiffMaintainsChangeMark()
+ enew!
+ call setline(1, ['a', 'b', 'c', 'd'])
+ diffthis
+ new
+ call setline(1, ['a', 'b', 'c', 'e'])
+ " Set '[ and '] marks
+ 2,3yank
+ call assert_equal([2, 3], [line("'["), line("']")])
+ " Verify they aren't affected by the implicit diff
+ diffthis
+ call assert_equal([2, 3], [line("'["), line("']")])
+ " Verify they aren't affected by an explicit diff
+ diffupdate
+ call assert_equal([2, 3], [line("'["), line("']")])
+ bwipe!
+ bwipe!
+ endfunc
+
+ set diffopt-=internal
+ call DiffMaintainsChangeMark()
+ set diffopt+=internal
+ call DiffMaintainsChangeMark()
+
+ set diffopt&
+ delfunc DiffMaintainsChangeMark
+endfunc
+
func Test_diff_rnu()
CheckScreendump
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index fc4e80f0d6..c1f74e7675 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1006,16 +1006,16 @@ func Test_edit_DROP()
endfunc
func Test_edit_CTRL_V()
- if has("ebcdic")
- return
- endif
+ CheckNotFeature ebcdic
+
new
call setline(1, ['abc'])
call cursor(2, 1)
+
" force some redraws
set showmode showcmd
- "call test_override_char_avail(1)
- " call test_override('ALL', 1)
+ " call test_override('char_avail', 1)
+
call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix')
call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$'))
@@ -1028,8 +1028,19 @@ func Test_edit_CTRL_V()
set norl
endif
- " call test_override('ALL', 0)
set noshowmode showcmd
+ " call test_override('char_avail', 0)
+
+ " No modifiers should be applied to the char typed using i_CTRL-V_digit.
+ call feedkeys(":append\<CR>\<C-V>76c\<C-V>76\<C-F2>\<C-V>u3c0j\<C-V>u3c0\<M-F3>\<CR>.\<CR>", 'tnix')
+ call assert_equal('LcL<C-F2>πjπ<M-F3>', getline(2))
+
+ if has('osx')
+ " A char with a modifier should not be a valid char for i_CTRL-V_digit.
+ call feedkeys("o\<C-V>\<D-j>\<C-V>\<D-1>\<C-V>\<D-o>\<C-V>\<D-x>\<C-V>\<D-u>", 'tnix')
+ call assert_equal('<D-j><D-1><D-o><D-x><D-u>', getline(3))
+ endif
+
bw!
endfunc
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 92e0559618..78663f7deb 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -98,4 +98,14 @@ func Test_ex_mode_count_overflow()
call delete('Xexmodescript')
endfunc
+func Test_ex_mode_large_indent()
+ new
+ set ts=500 ai
+ call setline(1, "\t")
+ exe "normal gQi\<CR>."
+ set ts=8 noai
+ bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 1d7fd3e385..5b10e691e5 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -283,6 +283,71 @@ function Test_printf_misc()
call assert_equal('🐍', printf('%.2S', '🐍🐍'))
call assert_equal('', printf('%.1S', '🐍🐍'))
+ call assert_equal('[ あいう]', printf('[%10.6S]', 'あいうえお'))
+ call assert_equal('[ あいうえ]', printf('[%10.8S]', 'あいうえお'))
+ call assert_equal('[あいうえお]', printf('[%10.10S]', 'あいうえお'))
+ call assert_equal('[あいうえお]', printf('[%10.12S]', 'あいうえお'))
+
+ call assert_equal('あいう', printf('%S', 'あいう'))
+ call assert_equal('あいう', printf('%#S', 'あいう'))
+
+ call assert_equal('あb', printf('%2S', 'あb'))
+ call assert_equal('あb', printf('%.4S', 'あb'))
+ call assert_equal('あ', printf('%.2S', 'あb'))
+ call assert_equal(' あb', printf('%4S', 'あb'))
+ call assert_equal('0あb', printf('%04S', 'あb'))
+ call assert_equal('あb ', printf('%-4S', 'あb'))
+ call assert_equal('あ ', printf('%-4.2S', 'あb'))
+
+ call assert_equal('aい', printf('%2S', 'aい'))
+ call assert_equal('aい', printf('%.4S', 'aい'))
+ call assert_equal('a', printf('%.2S', 'aい'))
+ call assert_equal(' aい', printf('%4S', 'aい'))
+ call assert_equal('0aい', printf('%04S', 'aい'))
+ call assert_equal('aい ', printf('%-4S', 'aい'))
+ call assert_equal('a ', printf('%-4.2S', 'aい'))
+
+ call assert_equal('[あいう]', printf('[%05S]', 'あいう'))
+ call assert_equal('[あいう]', printf('[%06S]', 'あいう'))
+ call assert_equal('[0あいう]', printf('[%07S]', 'あいう'))
+
+ call assert_equal('[あiう]', printf('[%05S]', 'あiう'))
+ call assert_equal('[0あiう]', printf('[%06S]', 'あiう'))
+ call assert_equal('[00あiう]', printf('[%07S]', 'あiう'))
+
+ call assert_equal('[0あい]', printf('[%05.4S]', 'あいう'))
+ call assert_equal('[00あい]', printf('[%06.4S]', 'あいう'))
+ call assert_equal('[000あい]', printf('[%07.4S]', 'あいう'))
+
+ call assert_equal('[00あi]', printf('[%05.4S]', 'あiう'))
+ call assert_equal('[000あi]', printf('[%06.4S]', 'あiう'))
+ call assert_equal('[0000あi]', printf('[%07.4S]', 'あiう'))
+
+ call assert_equal('[0あい]', printf('[%05.5S]', 'あいう'))
+ call assert_equal('[00あい]', printf('[%06.5S]', 'あいう'))
+ call assert_equal('[000あい]', printf('[%07.5S]', 'あいう'))
+
+ call assert_equal('[あiう]', printf('[%05.5S]', 'あiう'))
+ call assert_equal('[0あiう]', printf('[%06.5S]', 'あiう'))
+ call assert_equal('[00あiう]', printf('[%07.5S]', 'あiう'))
+
+ call assert_equal('[0000000000]', printf('[%010.0S]', 'あいう'))
+ call assert_equal('[0000000000]', printf('[%010.1S]', 'あいう'))
+ call assert_equal('[00000000あ]', printf('[%010.2S]', 'あいう'))
+ call assert_equal('[00000000あ]', printf('[%010.3S]', 'あいう'))
+ call assert_equal('[000000あい]', printf('[%010.4S]', 'あいう'))
+ call assert_equal('[000000あい]', printf('[%010.5S]', 'あいう'))
+ call assert_equal('[0000あいう]', printf('[%010.6S]', 'あいう'))
+ call assert_equal('[0000あいう]', printf('[%010.7S]', 'あいう'))
+
+ call assert_equal('[0000000000]', printf('[%010.1S]', 'あiう'))
+ call assert_equal('[00000000あ]', printf('[%010.2S]', 'あiう'))
+ call assert_equal('[0000000あi]', printf('[%010.3S]', 'あiう'))
+ call assert_equal('[0000000あi]', printf('[%010.4S]', 'あiう'))
+ call assert_equal('[00000あiう]', printf('[%010.5S]', 'あiう'))
+ call assert_equal('[00000あiう]', printf('[%010.6S]', 'あiう'))
+ call assert_equal('[00000あiう]', printf('[%010.7S]', 'あiう'))
+
call assert_equal('1%', printf('%d%%', 1))
endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 4da68539fb..8d6f9ded1d 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -76,6 +76,7 @@ let s:filename_checks = {
\ 'ave': ['file.ave'],
\ 'awk': ['file.awk', 'file.gawk'],
\ 'b': ['file.mch', 'file.ref', 'file.imp'],
+ \ 'basic': ['file.bas', 'file.bi', 'file.bm'],
\ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'],
\ 'bc': ['file.bc'],
\ 'bdf': ['file.bdf'],
@@ -186,43 +187,54 @@ let s:filename_checks = {
\ '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'],
+ \ 'freebasic': ['file.fb'],
\ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'],
\ 'fstab': ['fstab', 'mtab'],
+ \ 'fusion': ['file.fusion'],
\ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'],
\ 'gdb': ['.gdbinit', 'gdbinit'],
+ \ 'gdresource': ['file.tscn', 'file.tres'],
+ \ 'gdscript': ['file.gd'],
\ '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'],
\ 'gift': ['file.gift'],
- \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'],
- \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'],
+ \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'],
+ \ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'],
\ 'gitolite': ['gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'],
\ 'gitrebase': ['git-rebase-todo'],
\ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'],
\ 'gkrellmrc': ['gkrellmrc', 'gkrellmrc_x'],
+ \ 'glsl': ['file.glsl'],
\ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'],
- \ 'gnuplot': ['file.gpi'],
+ \ 'gnuplot': ['file.gpi', '.gnuplot'],
\ 'go': ['file.go'],
\ 'gomod': ['go.mod'],
+ \ 'gowork': ['go.work'],
\ 'gp': ['file.gp', '.gprc'],
\ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'],
\ 'grads': ['file.gs'],
+ \ 'graphql': ['file.graphql', 'file.graphqls', 'file.gql'],
\ 'gretl': ['file.gretl'],
\ 'groovy': ['file.gradle', 'file.groovy'],
\ 'group': ['any/etc/group', 'any/etc/group-', 'any/etc/group.edit', 'any/etc/gshadow', 'any/etc/gshadow-', 'any/etc/gshadow.edit', 'any/var/backups/group.bak', 'any/var/backups/gshadow.bak', '/etc/group', '/etc/group-', '/etc/group.edit', '/etc/gshadow', '/etc/gshadow-', '/etc/gshadow.edit', '/var/backups/group.bak', '/var/backups/gshadow.bak'],
\ 'grub': ['/boot/grub/menu.lst', '/boot/grub/grub.conf', '/etc/grub.conf', 'any/boot/grub/grub.conf', 'any/boot/grub/menu.lst', 'any/etc/grub.conf'],
\ 'gsp': ['file.gsp'],
\ 'gtkrc': ['.gtkrc', 'gtkrc', '.gtkrc-file', 'gtkrc-file'],
+ \ 'hack': ['file.hack', 'file.hackpartial'],
\ 'haml': ['file.haml'],
\ 'hamster': ['file.hsm'],
+ \ 'handlebars': ['file.hbs'],
\ 'haskell': ['file.hs', 'file.hsc', 'file.hs-boot', 'file.hsig'],
\ 'haste': ['file.ht'],
\ 'hastepreproc': ['file.htpp'],
\ 'hb': ['file.hb'],
+ \ 'hcl': ['file.hcl'],
\ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'],
+ \ 'heex': ['file.heex'],
\ 'hex': ['file.hex', 'file.h32'],
\ 'hgcommit': ['hg-editor-file.txt'],
+ \ 'hjson': ['file.hjson'],
\ 'hog': ['file.hog', 'snort.conf', 'vision.conf'],
\ 'hollywood': ['file.hws'],
\ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'],
@@ -264,6 +276,7 @@ let s:filename_checks = {
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'],
\ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'],
+ \ 'json5': ['file.json5'],
\ 'jsonc': ['file.jsonc'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],
@@ -277,6 +290,7 @@ let s:filename_checks = {
\ 'latte': ['file.latte', 'file.lte'],
\ 'ld': ['file.ld'],
\ 'ldif': ['file.ldif'],
+ \ 'ledger': ['file.ldg', 'file.ledger', 'file.journal'],
\ 'less': ['file.less'],
\ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'],
\ 'lftp': ['lftp.conf', '.lftprc', 'anylftp/rc', 'lftp/rc', 'some-lftp/rc'],
@@ -350,6 +364,7 @@ let s:filename_checks = {
\ 'netrc': ['.netrc'],
\ 'nginx': ['file.nginx', 'nginxfile.conf', 'filenginx.conf', 'any/etc/nginx/file', 'any/usr/local/nginx/conf/file', 'any/nginx/file.conf'],
\ 'ninja': ['file.ninja'],
+ \ 'nix': ['file.nix'],
\ 'nqc': ['file.nqc'],
\ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom', 'tmac.file'],
\ 'nsis': ['file.nsi', 'file.nsh'],
@@ -392,6 +407,7 @@ let s:filename_checks = {
\ 'ppd': ['file.ppd'],
\ 'ppwiz': ['file.it', 'file.ih'],
\ 'privoxy': ['file.action'],
+ \ 'prisma': ['file.prisma'],
\ 'proc': ['file.pc'],
\ 'procmail': ['.procmail', '.procmailrc'],
\ 'prolog': ['file.pdb'],
@@ -402,10 +418,12 @@ let s:filename_checks = {
\ 'ps1xml': ['file.ps1xml'],
\ 'psf': ['file.psf'],
\ 'psl': ['file.psl'],
+ \ 'pug': ['file.pug'],
\ 'puppet': ['file.pp'],
\ 'pyret': ['file.arr'],
\ 'pyrex': ['file.pyx', 'file.pxd'],
\ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'],
+ \ 'ql': ['file.ql', 'file.qll'],
\ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'],
\ 'radiance': ['file.rad', 'file.mat'],
\ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'],
@@ -446,7 +464,7 @@ let s:filename_checks = {
\ 'sdc': ['file.sdc'],
\ 'sdl': ['file.sdl', 'file.pr'],
\ 'sed': ['file.sed'],
- \ 'sensors': ['/etc/sensors.conf', '/etc/sensors3.conf', 'any/etc/sensors.conf', 'any/etc/sensors3.conf'],
+ \ 'sensors': ['/etc/sensors.conf', '/etc/sensors3.conf', '/etc/sensors.d/file', 'any/etc/sensors.conf', 'any/etc/sensors3.conf', 'any/etc/sensors.d/file'],
\ 'services': ['/etc/services', 'any/etc/services'],
\ 'setserial': ['/etc/serial.conf', 'any/etc/serial.conf'],
\ 'sh': ['.bashrc', 'file.bash', '/usr/share/doc/bash-completion/filter.sh','/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf'],
@@ -487,6 +505,7 @@ let s:filename_checks = {
\ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'],
\ 'stp': ['file.stp'],
\ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'],
+ \ 'surface': ['file.sface'],
\ 'svg': ['file.svg'],
\ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'],
\ 'swift': ['file.swift'],
@@ -500,8 +519,10 @@ let s:filename_checks = {
\ 'taskdata': ['pending.data', 'completed.data', 'undo.data'],
\ 'taskedit': ['file.task'],
\ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc'],
+ \ 'teal': ['file.tl'],
\ 'teraterm': ['file.ttl'],
\ 'terminfo': ['file.ti'],
+ \ 'terraform': ['file.tfvars'],
\ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'],
\ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'],
\ 'texmf': ['texmf.cnf'],
@@ -509,6 +530,7 @@ let s:filename_checks = {
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
\ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
+ \ 'tla': ['file.tla'],
\ 'tli': ['file.tli'],
\ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf', '.tmux-file.conf', '.tmux.conf', 'tmux-file.conf', 'tmux.conf', 'tmux.conf.local'],
\ 'toml': ['file.toml', 'Gopkg.lock', 'Pipfile', '/home/user/.cargo/config'],
@@ -569,6 +591,7 @@ let s:filename_checks = {
\ 'xslt': ['file.xsl', 'file.xslt'],
\ 'yacc': ['file.yy', 'file.yxx', 'file.y++'],
\ 'yaml': ['file.yaml', 'file.yml'],
+ \ 'yang': ['file.yang'],
\ 'raml': ['file.raml'],
\ 'z8a': ['file.z8a'],
\ 'zig': ['file.zig'],
@@ -653,7 +676,7 @@ let s:script_checks = {
\ ['#!/path/nodejs'],
\ ['#!/path/rhino']],
\ 'bc': [['#!/path/bc']],
- \ 'sed': [['#!/path/sed']],
+ \ 'sed': [['#!/path/sed'], ['#n'], ['#n comment']],
\ 'ocaml': [['#!/path/ocaml']],
\ 'awk': [['#!/path/awk'],
\ ['#!/path/gawk']],
@@ -723,6 +746,24 @@ func Test_hook_file()
filetype off
endfunc
+func Test_tf_file()
+ filetype on
+
+ call writefile([';;; TF MUD client is super duper cool'], 'Xfile.tf')
+ split Xfile.tf
+ call assert_equal('tf', &filetype)
+ bwipe!
+
+ call writefile(['provider "azurerm" {'], 'Xfile.tf')
+ split Xfile.tf
+ call assert_equal('terraform', &filetype)
+ bwipe!
+
+ call delete('Xfile.tf')
+ filetype off
+endfunc
+
+
func Test_ts_file()
filetype on
@@ -1042,7 +1083,169 @@ func Test_dep3patch_file()
call assert_notequal('dep3patch', &filetype)
bwipe!
- call delete('debian/patches', 'rf')
+ call delete('debian', 'rf')
+endfunc
+
+func Test_patch_file()
+ filetype on
+
+ call writefile([], 'Xfile.patch')
+ split Xfile.patch
+ call assert_equal('diff', &filetype)
+ bwipe!
+
+ call writefile(['From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch')
+ split Xfile.patch
+ call assert_equal('gitsendemail', &filetype)
+ bwipe!
+
+ call writefile(['From 0000000000000000000000000000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch')
+ split Xfile.patch
+ call assert_equal('gitsendemail', &filetype)
+ bwipe!
+
+ call delete('Xfile.patch')
+ filetype off
+endfunc
+
+func Test_git_file()
+ filetype on
+
+ call assert_true(mkdir('Xrepo.git', 'p'))
+
+ call writefile([], 'Xrepo.git/HEAD')
+ split Xrepo.git/HEAD
+ call assert_equal('', &filetype)
+ bwipe!
+
+ call writefile(['0000000000000000000000000000000000000000'], 'Xrepo.git/HEAD')
+ split Xrepo.git/HEAD
+ call assert_equal('git', &filetype)
+ bwipe!
+
+ call writefile(['0000000000000000000000000000000000000000000000000000000000000000'], 'Xrepo.git/HEAD')
+ split Xrepo.git/HEAD
+ call assert_equal('git', &filetype)
+ bwipe!
+
+ call writefile(['ref: refs/heads/master'], 'Xrepo.git/HEAD')
+ split Xrepo.git/HEAD
+ call assert_equal('git', &filetype)
+ bwipe!
+
+ call delete('Xrepo.git', 'rf')
+ filetype off
+endfunc
+
+func Test_foam_file()
+ filetype on
+ call assert_true(mkdir('0', 'p'))
+ call assert_true(mkdir('0.orig', 'p'))
+
+ call writefile(['FoamFile {', ' object something;'], 'Xfile1Dict')
+ split Xfile1Dict
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call writefile(['FoamFile {', ' object something;'], 'Xfile1Dict.something')
+ split Xfile1Dict.something
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call writefile(['FoamFile {', ' object something;'], 'XfileProperties')
+ split XfileProperties
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call writefile(['FoamFile {', ' object something;'], 'XfileProperties.something')
+ split XfileProperties.something
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call writefile(['FoamFile {', ' object something;'], 'XfileProperties')
+ split XfileProperties
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call writefile(['FoamFile {', ' object something;'], 'XfileProperties.something')
+ split XfileProperties.something
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call writefile(['FoamFile {', ' object something;'], '0/Xfile')
+ split 0/Xfile
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call writefile(['FoamFile {', ' object something;'], '0.orig/Xfile')
+ split 0.orig/Xfile
+ call assert_equal('foam', &filetype)
+ bwipe!
+
+ call delete('0', 'rf')
+ call delete('0.orig', 'rf')
+ filetype off
+endfunc
+
+func Test_bas_file()
+ filetype on
+
+ call writefile(['looks like BASIC'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('basic', &filetype)
+ bwipe!
+
+ " Test dist#ft#FTbas()
+
+ let g:filetype_bas = 'freebasic'
+ split Xfile.bas
+ call assert_equal('freebasic', &filetype)
+ bwipe!
+ unlet g:filetype_bas
+
+ " FreeBASIC
+
+ call writefile(["/' FreeBASIC multiline comment '/"], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('freebasic', &filetype)
+ bwipe!
+
+ call writefile(['#define TESTING'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('freebasic', &filetype)
+ bwipe!
+
+ call writefile(['option byval'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('freebasic', &filetype)
+ bwipe!
+
+ call writefile(['extern "C"'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('freebasic', &filetype)
+ bwipe!
+
+ " QB64
+
+ call writefile(['$LET TESTING = 1'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('qb64', &filetype)
+ bwipe!
+
+ call writefile(['OPTION _EXPLICIT'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('qb64', &filetype)
+ bwipe!
+
+ " Visual Basic
+
+ call writefile(['Attribute VB_NAME = "Testing"'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('vb', &filetype)
+ bwipe!
+
+ call delete('Xfile.bas')
+ filetype off
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype_lua.vim b/src/nvim/testdir/test_filetype_lua.vim
new file mode 100644
index 0000000000..f73e4ca33f
--- /dev/null
+++ b/src/nvim/testdir/test_filetype_lua.vim
@@ -0,0 +1,2 @@
+let g:do_filetype_lua = 1
+source test_filetype.vim
diff --git a/src/nvim/testdir/test_filter_map.vim b/src/nvim/testdir/test_filter_map.vim
index a52a66ac2f..1cd3a2287b 100644
--- a/src/nvim/testdir/test_filter_map.vim
+++ b/src/nvim/testdir/test_filter_map.vim
@@ -88,4 +88,14 @@ func Test_map_filter_fails()
call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:')
endfunc
+func Test_map_and_modify()
+ let l = ["abc"]
+ " cannot change the list halfway a map()
+ call assert_fails('call map(l, "remove(l, 0)[0]")', 'E741:')
+
+ let d = #{a: 1, b: 2, c: 3}
+ call assert_fails('call map(d, "remove(d, v:key)[0]")', 'E741:')
+ call assert_fails('echo map(d, {k,v -> remove(d, k)})', 'E741:')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index 8e59efd22d..977dad6a45 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -101,4 +101,12 @@ func Test_helptag_cmd()
call delete('Xdir', 'rf')
endfunc
+func Test_help_long_argument()
+ try
+ exe 'help \%' .. repeat('0', 1021)
+ catch
+ call assert_match("E149:", v:exception)
+ endtry
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index ce75799551..6803271c03 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -445,6 +445,28 @@ func Test_issue_7021()
set completeslash=
endfunc
+func Test_pum_stopped_by_timer()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['hello', 'hullo', 'heeee', ''])
+ func StartCompl()
+ call timer_start(100, { -> execute('stopinsert') })
+ call feedkeys("Gah\<C-N>")
+ endfunc
+ END
+
+ call writefile(lines, 'Xpumscript')
+ let buf = RunVimInTerminal('-S Xpumscript', #{rows: 12})
+ call term_sendkeys(buf, ":call StartCompl()\<CR>")
+ call TermWait(buf, 200)
+ call term_sendkeys(buf, "k")
+ call VerifyScreenDump(buf, 'Test_pum_stopped_by_timer', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xpumscript')
+endfunc
+
func Test_pum_with_folds_two_tabs()
CheckScreendump
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index 0bcbd9c4a5..c6e2ebd406 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -1,6 +1,8 @@
" Tests for 'listchars' display with 'list' and :list
+source check.vim
source view_util.vim
+source screendump.vim
func Test_listchars()
enew!
@@ -517,4 +519,34 @@ func Test_listchars_window_local()
set list& listchars&
endfunc
+func Test_listchars_foldcolumn()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['aaa', '', 'a', 'aaaaaa'])
+ vsplit
+ vsplit
+ windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:<
+ END
+ call writefile(lines, 'XTest_listchars')
+
+ let buf = RunVimInTerminal('-S XTest_listchars', {'rows': 10, 'cols': 60})
+
+ call term_sendkeys(buf, "13\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_01', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_02', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_03', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_04', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_05', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_listchars')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim
index c38e0c5f3c..1f100d6244 100644
--- a/src/nvim/testdir/test_listlbr_utf8.vim
+++ b/src/nvim/testdir/test_listlbr_utf8.vim
@@ -69,6 +69,16 @@ func Test_nolinebreak_with_list()
call s:close_windows()
endfunc
+" this was causing a crash
+func Test_linebreak_with_list_and_tabs()
+ set linebreak list listchars=tab:⇤\ ⇥ tabstop=100
+ new
+ call setline(1, "\t\t\ttext")
+ redraw
+ bwipe!
+ set nolinebreak nolist listchars&vim tabstop=8
+endfunc
+
func Test_linebreak_with_nolist()
call s:test_windows('setl nolist')
call setline(1, "\t*mask = nil;")
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index b3035d73ce..4ef42946cb 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -207,6 +207,21 @@ func Test_mark_error()
call assert_fails('mark _', 'E191:')
endfunc
+" Test for :lockmarks when pasting content
+func Test_lockmarks_with_put()
+ new
+ call append(0, repeat(['sky is blue'], 4))
+ normal gg
+ 1,2yank r
+ put r
+ normal G
+ lockmarks put r
+ call assert_equal(2, line("'["))
+ call assert_equal(3, line("']"))
+
+ bwipe!
+endfunc
+
" Test for the getmarklist() function
func Test_getmarklist()
new
@@ -231,3 +246,5 @@ func Test_getmarklist()
call assert_equal([], {}->getmarklist())
close!
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index aff22f5d01..5b7cf6fee5 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1812,7 +1812,15 @@ fun! Test_normal33_g_cmd2()
call assert_equal(87, col('.'))
call assert_equal('E', getreg(0))
+ " Test for gM with Tab characters
+ call setline('.', "\ta\tb\tc\td\te\tf")
+ norm! gMyl
+ call assert_equal(6, col('.'))
+ call assert_equal("c", getreg(0))
+
" Test for g Ctrl-G
+ call setline('.', lineC)
+ norm! 60gMyl
set ff=unix
let a=execute(":norm! g\<c-g>")
call assert_match('Col 87 of 144; Line 2 of 2; Word 1 of 1; Byte 88 of 146', a)
@@ -2759,4 +2767,16 @@ func Test_normal_count_after_operator()
bw!
endfunc
+func Test_normal_gj_on_extra_wide_char()
+ new | 25vsp
+ let text='1 foooooooo ar e ins‍zwe1 foooooooo ins‍zwei' .
+ \ ' i drei vier fünf sechs sieben acht un zehn elf zwöfl' .
+ \ ' dreizehn v ierzehn fünfzehn'
+ put =text
+ call cursor(2,1)
+ norm! gj
+ call assert_equal([0,2,25,0], getpos('.'))
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 5946732937..2312df5450 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -51,7 +51,7 @@ func Test_wildoptions()
call assert_equal('tagfile', &wildoptions)
endfunc
-function! Test_options()
+func Test_options_command()
let caught = 'ok'
try
options
@@ -88,7 +88,7 @@ function! Test_options()
" close option-window
close
-endfunction
+endfunc
function! Test_path_keep_commas()
" Test that changing 'path' keeps two commas.
@@ -368,6 +368,13 @@ func Test_set_all()
set tw& iskeyword& splitbelow&
endfunc
+func Test_set_one_column()
+ let out_mult = execute('set all')->split("\n")
+ let out_one = execute('set! all')->split("\n")
+ " one column should be two to four times as many lines
+ call assert_inrange(len(out_mult) * 2, len(out_mult) * 4, len(out_one))
+endfunc
+
func Test_set_values()
" The file is only generated when running "make test" in the src directory.
if filereadable('opt_test.vim')
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 712f1e6025..a92f7e1192 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -787,4 +787,12 @@ func Test_regexp_error()
set re&
endfunc
+func Test_using_mark_position()
+ " this was using freed memory
+ new
+ norm O0
+ call assert_fails("s/\\%')", 'E486:')
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim
index d8d5797dcf..c568805f87 100644
--- a/src/nvim/testdir/test_regexp_utf8.vim
+++ b/src/nvim/testdir/test_regexp_utf8.vim
@@ -590,4 +590,12 @@ func Test_match_char_class_upper()
bwipe!
endfunc
+func Test_match_invalid_byte()
+ call writefile(0z630a.765d30aa0a.2e0a.790a.4030, 'Xinvalid')
+ new
+ source Xinvalid
+ bwipe!
+ call delete('Xinvalid')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 84a5aca3d5..2e92d9aa2f 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -1,6 +1,4 @@
-"
" Tests for register operations
-"
source check.vim
source view_util.vim
@@ -13,6 +11,8 @@ func Test_aaa_empty_reg_test()
call assert_fails('normal @!', 'E354:')
call assert_fails('normal @:', 'E30:')
call assert_fails('normal @.', 'E29:')
+ call assert_fails('put /', 'E35:')
+ call assert_fails('put .', 'E29:')
endfunc
func Test_yank_shows_register()
@@ -119,6 +119,17 @@ func Test_recording_esc_sequence()
endif
endfunc
+func Test_recording_with_select_mode()
+ new
+ call feedkeys("qacc12345\<Esc>gH98765\<Esc>q", "tx")
+ call assert_equal("98765", getline(1))
+ call assert_equal("cc12345\<Esc>gH98765\<Esc>", @a)
+ call setline(1, 'asdf')
+ normal! @a
+ call assert_equal("98765", getline(1))
+ bwipe!
+endfunc
+
" Test for executing the last used register (@)
func Test_last_used_exec_reg()
" Test for the @: command
@@ -141,6 +152,14 @@ func Test_last_used_exec_reg()
normal @@
call assert_equal('EditEdit', a)
+ " Test for repeating the last command-line in visual mode
+ call append(0, 'register')
+ normal gg
+ let @r = ''
+ call feedkeys("v:yank R\<CR>", 'xt')
+ call feedkeys("v@:", 'xt')
+ call assert_equal("\nregister\nregister\n", @r)
+
enew!
endfunc
@@ -164,6 +183,19 @@ func Test_get_register()
call assert_equal('', getregtype('!'))
+ " Test for inserting an invalid register content
+ call assert_beeps('exe "normal i\<C-R>!"')
+
+ " Test for inserting a register with multiple lines
+ call deletebufline('', 1, '$')
+ call setreg('r', ['a', 'b'])
+ exe "normal i\<C-R>r"
+ call assert_equal(['a', 'b', ''], getline(1, '$'))
+
+ " Test for inserting a multi-line register in the command line
+ call feedkeys(":\<C-R>r\<Esc>", 'xt')
+ call assert_equal("a\rb", histget(':', -1)) " Modified because of #6137
+
enew!
endfunc
@@ -187,9 +219,200 @@ func Test_set_register()
call setreg('=', 'b', 'a')
call assert_equal('regwrite', getreg('='))
+ " Test for settting a list of lines to special registers
+ call setreg('/', [])
+ call assert_equal('', @/)
+ call setreg('=', [])
+ call assert_equal('', @=)
+ call assert_fails("call setreg('/', ['a', 'b'])", 'E883:')
+ call assert_fails("call setreg('=', ['a', 'b'])", 'E883:')
+ call assert_equal(0, setreg('_', ['a', 'b']))
+
+ " Test for recording to a invalid register
+ call assert_beeps('normal q$')
+
+ " Appending to a register when recording
+ call append(0, "text for clipboard test")
+ normal gg
+ call feedkeys('qrllq', 'xt')
+ call feedkeys('qRhhq', 'xt')
+ call assert_equal('llhh', getreg('r'))
+
+ " Appending a list of characters to a register from different lines
+ let @r = ''
+ call append(0, ['abcdef', '123456'])
+ normal gg"ry3l
+ call cursor(2, 4)
+ normal "Ry3l
+ call assert_equal('abc456', @r)
+
+ " Test for gP with multiple lines selected using characterwise motion
+ %delete
+ call append(0, ['vim editor', 'vim editor'])
+ let @r = ''
+ exe "normal ggwy/vim /e\<CR>gP"
+ call assert_equal(['vim editor', 'vim editor', 'vim editor'], getline(1, 3))
+
+ " Test for gP with . register
+ %delete
+ normal iabc
+ normal ".gp
+ call assert_equal('abcabc', getline(1))
+ normal 0".gP
+ call assert_equal('abcabcabc', getline(1))
+
enew!
endfunc
+" Test for clipboard registers (* and +)
+func Test_clipboard_regs()
+ throw 'skipped: needs clipboard=autoselect,autoselectplus'
+
+ CheckNotGui
+ CheckFeature clipboard_working
+
+ new
+ call append(0, "text for clipboard test")
+ normal gg"*yiw
+ call assert_equal('text', getreg('*'))
+ normal gg2w"+yiw
+ call assert_equal('clipboard', getreg('+'))
+
+ " Test for replacing the clipboard register contents
+ set clipboard=unnamed
+ let @* = 'food'
+ normal ggviw"*p
+ call assert_equal('text', getreg('*'))
+ call assert_equal('food for clipboard test', getline(1))
+ normal ggviw"*p
+ call assert_equal('food', getreg('*'))
+ call assert_equal('text for clipboard test', getline(1))
+
+ " Test for replacing the selection register contents
+ set clipboard=unnamedplus
+ let @+ = 'food'
+ normal ggviw"+p
+ call assert_equal('text', getreg('+'))
+ call assert_equal('food for clipboard test', getline(1))
+ normal ggviw"+p
+ call assert_equal('food', getreg('+'))
+ call assert_equal('text for clipboard test', getline(1))
+
+ " Test for auto copying visually selected text to clipboard register
+ call setline(1, "text for clipboard test")
+ let @* = ''
+ set clipboard=autoselect
+ normal ggwwviwy
+ call assert_equal('clipboard', @*)
+
+ " Test for auto copying visually selected text to selection register
+ let @+ = ''
+ set clipboard=autoselectplus
+ normal ggwviwy
+ call assert_equal('for', @+)
+
+ set clipboard&vim
+ bwipe!
+endfunc
+
+" Test for restarting the current mode (insert or virtual replace) after
+" executing the contents of a register
+func Test_put_reg_restart_mode()
+ new
+ call append(0, 'editor')
+ normal gg
+ let @r = "ivim \<Esc>"
+ call feedkeys("i\<C-O>@r\<C-R>=mode()\<CR>", 'xt')
+ call assert_equal('vimi editor', getline(1))
+
+ call setline(1, 'editor')
+ normal gg
+ call feedkeys("gR\<C-O>@r\<C-R>=mode()\<CR>", 'xt')
+ call assert_equal('vimReditor', getline(1))
+
+ bwipe!
+endfunc
+
+" Test for getting register info
+func Test_get_reginfo()
+ enew
+ call setline(1, ['foo', 'bar'])
+
+ exe 'norm! "zyy'
+ let info = getreginfo('"')
+ call assert_equal('z', info.points_to)
+ call setreg('y', 'baz')
+ call assert_equal('z', getreginfo('').points_to)
+ call setreg('y', { 'isunnamed': v:true })
+ call assert_equal('y', getreginfo('"').points_to)
+
+ exe '$put'
+ call assert_equal(getreg('y'), getline(3))
+ call setreg('', 'qux')
+ call assert_equal('0', getreginfo('').points_to)
+ call setreg('x', 'quux')
+ call assert_equal('0', getreginfo('').points_to)
+
+ let info = getreginfo('')
+ call assert_equal(getreg('', 1, 1), info.regcontents)
+ call assert_equal(getregtype(''), info.regtype)
+
+ exe "norm! 0\<c-v>e" .. '"zy'
+ let info = getreginfo('z')
+ call assert_equal(getreg('z', 1, 1), info.regcontents)
+ call assert_equal(getregtype('z'), info.regtype)
+ call assert_equal(1, +info.isunnamed)
+
+ let info = getreginfo('"')
+ call assert_equal('z', info.points_to)
+
+ bwipe!
+endfunc
+
+" Test for restoring register with dict from getreginfo
+func Test_set_register_dict()
+ enew!
+
+ call setreg('"', #{ regcontents: ['one', 'two'],
+ \ regtype: 'V', points_to: 'z' })
+ call assert_equal(['one', 'two'], getreg('"', 1, 1))
+ let info = getreginfo('"')
+ call assert_equal('z', info.points_to)
+ call assert_equal('V', info.regtype)
+ call assert_equal(1, +getreginfo('z').isunnamed)
+
+ call setreg('x', #{ regcontents: ['three', 'four'],
+ \ regtype: 'v', isunnamed: v:true })
+ call assert_equal(['three', 'four'], getreg('"', 1, 1))
+ let info = getreginfo('"')
+ call assert_equal('x', info.points_to)
+ call assert_equal('v', info.regtype)
+ call assert_equal(1, +getreginfo('x').isunnamed)
+
+ call setreg('y', #{ regcontents: 'five',
+ \ regtype: "\<c-v>", isunnamed: v:false })
+ call assert_equal("\<c-v>4", getreginfo('y').regtype)
+ call assert_equal(0, +getreginfo('y').isunnamed)
+ call assert_equal(['three', 'four'], getreg('"', 1, 1))
+ call assert_equal('x', getreginfo('"').points_to)
+
+ call setreg('"', #{ regcontents: 'six' })
+ call assert_equal('0', getreginfo('"').points_to)
+ call assert_equal(1, +getreginfo('0').isunnamed)
+ call assert_equal(['six'], getreginfo('0').regcontents)
+ call assert_equal(['six'], getreginfo('"').regcontents)
+
+ let @x = 'one'
+ call setreg('x', {})
+ call assert_equal(1, len(split(execute('reg x'), '\n')))
+
+ call assert_fails("call setreg('0', #{regtype: 'V'}, 'v')", 'E118:')
+ call assert_fails("call setreg('0', #{regtype: 'X'})", 'E475:')
+ call assert_fails("call setreg('0', #{regtype: 'vy'})", 'E475:')
+
+ bwipe!
+endfunc
+
func Test_v_register()
enew
call setline(1, 'nothing')
@@ -288,84 +511,25 @@ func Test_insert_small_delete()
bwipe!
endfunc
-" Test for getting register info
-func Test_get_reginfo()
- enew
- call setline(1, ['foo', 'bar'])
-
- exe 'norm! "zyy'
- let info = getreginfo('"')
- call assert_equal('z', info.points_to)
- call setreg('y', 'baz')
- call assert_equal('z', getreginfo('').points_to)
- call setreg('y', { 'isunnamed': v:true })
- call assert_equal('y', getreginfo('"').points_to)
-
- exe '$put'
- call assert_equal(getreg('y'), getline(3))
- call setreg('', 'qux')
- call assert_equal('0', getreginfo('').points_to)
- call setreg('x', 'quux')
- call assert_equal('0', getreginfo('').points_to)
-
- let info = getreginfo('')
- call assert_equal(getreg('', 1, 1), info.regcontents)
- call assert_equal(getregtype(''), info.regtype)
-
- exe "norm! 0\<c-v>e" .. '"zy'
- let info = getreginfo('z')
- call assert_equal(getreg('z', 1, 1), info.regcontents)
- call assert_equal(getregtype('z'), info.regtype)
- call assert_equal(1, +info.isunnamed)
-
- let info = getreginfo('"')
- call assert_equal('z', info.points_to)
+func Test_record_in_select_mode()
+ new
+ call setline(1, 'text')
+ sil norm q00
+ sil norm q
+ call assert_equal('0ext', getline(1))
+
+ %delete
+ let @r = ''
+ call setline(1, ['abc', 'abc', 'abc'])
+ smap <F2> <Right><Right>,
+ call feedkeys("qrgh\<F2>Dk\<Esc>q", 'xt')
+ call assert_equal("gh\<F2>Dk\<Esc>", @r)
+ norm j0@rj0@@
+ call assert_equal([',Dk', ',Dk', ',Dk'], getline(1, 3))
+ sunmap <F2>
bwipe!
endfunc
-" Test for restoring register with dict from getreginfo
-func Test_set_register_dict()
- enew!
-
- call setreg('"', #{ regcontents: ['one', 'two'],
- \ regtype: 'V', points_to: 'z' })
- call assert_equal(['one', 'two'], getreg('"', 1, 1))
- let info = getreginfo('"')
- call assert_equal('z', info.points_to)
- call assert_equal('V', info.regtype)
- call assert_equal(1, +getreginfo('z').isunnamed)
-
- call setreg('x', #{ regcontents: ['three', 'four'],
- \ regtype: 'v', isunnamed: v:true })
- call assert_equal(['three', 'four'], getreg('"', 1, 1))
- let info = getreginfo('"')
- call assert_equal('x', info.points_to)
- call assert_equal('v', info.regtype)
- call assert_equal(1, +getreginfo('x').isunnamed)
-
- call setreg('y', #{ regcontents: 'five',
- \ regtype: "\<c-v>", isunnamed: v:false })
- call assert_equal("\<c-v>4", getreginfo('y').regtype)
- call assert_equal(0, +getreginfo('y').isunnamed)
- call assert_equal(['three', 'four'], getreg('"', 1, 1))
- call assert_equal('x', getreginfo('"').points_to)
-
- call setreg('"', #{ regcontents: 'six' })
- call assert_equal('0', getreginfo('"').points_to)
- call assert_equal(1, +getreginfo('0').isunnamed)
- call assert_equal(['six'], getreginfo('0').regcontents)
- call assert_equal(['six'], getreginfo('"').regcontents)
-
- let @x = 'one'
- call setreg('x', {})
- call assert_equal(1, len(split(execute('reg x'), '\n')))
-
- call assert_fails("call setreg('0', #{regtype: 'V'}, 'v')", 'E118:')
- call assert_fails("call setreg('0', #{regtype: 'X'})", 'E475:')
- call assert_fails("call setreg('0', #{regtype: 'vy'})", 'E475:')
-
- bwipe!
-endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim
index f11a32bade..e7b8946ccf 100644
--- a/src/nvim/testdir/test_retab.vim
+++ b/src/nvim/testdir/test_retab.vim
@@ -74,4 +74,7 @@ endfunc
func Test_retab_error()
call assert_fails('retab -1', 'E487:')
call assert_fails('retab! -1', 'E487:')
+ call assert_fails('ret -1000', 'E487:')
+ call assert_fails('ret 10000', 'E475:')
+ call assert_fails('ret 80000000000000000000', 'E475:')
endfunc
diff --git a/src/nvim/testdir/test_scriptnames.vim b/src/nvim/testdir/test_scriptnames.vim
index fc6c910bfa..44ec146666 100644
--- a/src/nvim/testdir/test_scriptnames.vim
+++ b/src/nvim/testdir/test_scriptnames.vim
@@ -23,4 +23,10 @@ func Test_scriptnames()
bwipe
call delete('Xscripting')
+
+ let msgs = execute('messages')
+ scriptnames
+ call assert_equal(msgs, execute('messages'))
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim
new file mode 100644
index 0000000000..b483841060
--- /dev/null
+++ b/src/nvim/testdir/test_selectmode.vim
@@ -0,0 +1,57 @@
+" Test for Select-mode
+
+source shared.vim
+
+" Test for selecting a register with CTRL-R
+func Test_selectmode_register()
+ new
+
+ " Default behavior: use unnamed register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " Use the black hole register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>_a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('bar', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " Invalid register: use unnamed register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>?a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " Use unnamed register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>\"a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " use specicifed register, unnamed register is also written
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>aa"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('f', getreg('a'))
+
+ bw!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index cf0faeee31..1ecb5c8070 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -768,6 +768,14 @@ func Test_spell_screendump()
call delete('XtestSpell')
endfunc
+func Test_spell_single_word()
+ new
+ silent! norm 0R00
+ spell!
+ silent 0norm 0r$ Dvz=
+ bwipe!
+endfunc
+
let g:test_data_aff1 = [
\"SET ISO8859-1",
\"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ",
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index a3e4dcdd25..f40c9ae097 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -186,7 +186,16 @@ func Test_statusline()
set virtualedit=all
norm 10|
call assert_match('^10,-10\s*$', s:get_statusline())
+ set list
+ call assert_match('^10,-10\s*$', s:get_statusline())
set virtualedit&
+ exe "norm A\<Tab>\<Tab>a\<Esc>"
+ " In list mode a <Tab> is shown as "^I", which is 2-wide.
+ call assert_match('^9,-9\s*$', s:get_statusline())
+ set list&
+ " Now the second <Tab> ends at the 16th screen column.
+ call assert_match('^17,-17\s*$', s:get_statusline())
+ undo
" %w: Preview window flag, text is "[Preview]".
" %W: Preview window flag, text is ",PRV".
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index aae315b2c5..09eed4e10d 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -379,4 +379,27 @@ func Test_timer_invalid_callback()
call assert_fails('call timer_start(0, "0")', 'E921')
endfunc
+func Test_timer_using_win_execute_undo_sync()
+ let bufnr1 = bufnr()
+ new
+ let g:bufnr2 = bufnr()
+ let g:winid = win_getid()
+ exe "buffer " .. bufnr1
+ wincmd w
+ call setline(1, ['test'])
+ autocmd InsertEnter * call timer_start(100, { -> win_execute(g:winid, 'buffer ' .. g:bufnr2) })
+ call timer_start(200, { -> feedkeys("\<CR>bbbb\<Esc>") })
+ call feedkeys("Oaaaa", 'x!t')
+ " will hang here until the second timer fires
+ call assert_equal(['aaaa', 'bbbb', 'test'], getline(1, '$'))
+ undo
+ call assert_equal(['test'], getline(1, '$'))
+
+ bwipe!
+ bwipe!
+ unlet g:winid
+ unlet g:bufnr2
+ au! InsertEnter
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim
index 29e578ac6d..481959d43d 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -269,10 +269,10 @@ endfunc
func Test_CmdCompletion()
call feedkeys(":com -\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"com -addr bang bar buffer complete count nargs range register', @:)
+ call assert_equal('"com -addr bang bar buffer complete count keepscript nargs range register', @:)
call feedkeys(":com -nargs=0 -\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"com -nargs=0 -addr bang bar buffer complete count nargs range register', @:)
+ call assert_equal('"com -nargs=0 -addr bang bar buffer complete count keepscript nargs range register', @:)
call feedkeys(":com -nargs=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"com -nargs=* + 0 1 ?', @:)
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 0818c2e4b0..36776d5a64 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -1,5 +1,6 @@
" Tests for Unicode manipulations
+source check.vim
source view_util.vim
" Visual block Insert adjusts for multi-byte char
@@ -148,4 +149,55 @@ func Test_print_overlong()
bwipe!
endfunc
+func Test_recording_with_select_mode_utf8()
+ call Run_test_recording_with_select_mode_utf8()
+endfunc
+
+func Run_test_recording_with_select_mode_utf8()
+ new
+
+ " No escaping
+ call feedkeys("qacc12345\<Esc>gH哦\<Esc>q", "tx")
+ call assert_equal("哦", getline(1))
+ call assert_equal("cc12345\<Esc>gH哦\<Esc>", @a)
+ call setline(1, 'asdf')
+ normal! @a
+ call assert_equal("哦", getline(1))
+
+ " 固 is 0xE5 0x9B 0xBA where 0x9B is CSI
+ call feedkeys("qacc12345\<Esc>gH固\<Esc>q", "tx")
+ call assert_equal("固", getline(1))
+ call assert_equal("cc12345\<Esc>gH固\<Esc>", @a)
+ call setline(1, 'asdf')
+ normal! @a
+ call assert_equal("固", getline(1))
+
+ " 四 is 0xE5 0x9B 0x9B where 0x9B is CSI
+ call feedkeys("qacc12345\<Esc>gH四\<Esc>q", "tx")
+ call assert_equal("四", getline(1))
+ call assert_equal("cc12345\<Esc>gH四\<Esc>", @a)
+ call setline(1, 'asdf')
+ normal! @a
+ call assert_equal("四", getline(1))
+
+ " 倒 is 0xE5 0x80 0x92 where 0x80 is K_SPECIAL
+ call feedkeys("qacc12345\<Esc>gH倒\<Esc>q", "tx")
+ call assert_equal("倒", getline(1))
+ call assert_equal("cc12345\<Esc>gH倒\<Esc>", @a)
+ call setline(1, 'asdf')
+ normal! @a
+ call assert_equal("倒", getline(1))
+
+ bwipe!
+endfunc
+
+" This must be done as one of the last tests, because it starts the GUI, which
+" cannot be undone.
+func Test_zz_recording_with_select_mode_utf8_gui()
+ CheckCanRunGui
+
+ gui -f
+ call Run_test_recording_with_select_mode_utf8()
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index 8f992f7501..250b896532 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -81,6 +81,133 @@ func Test_edit_change()
normal Cx
call assert_equal('x', getline(1))
bwipe!
+ set virtualedit=
+endfunc
+
+" Tests for pasting at the beginning, end and middle of a tab character
+" in virtual edit mode.
+func Test_paste_in_tab()
+ new
+ call append(0, '')
+ set virtualedit=all
+
+ " Tests for pasting a register with characterwise mode type
+ call setreg('"', 'xyz', 'c')
+
+ " paste (p) unnamed register at the beginning of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 0)
+ normal p
+ call assert_equal('a xyz b', getline(1))
+
+ " paste (P) unnamed register at the beginning of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 0)
+ normal P
+ call assert_equal("axyz\tb", getline(1))
+
+ " paste (p) unnamed register at the end of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 6)
+ normal p
+ call assert_equal("a\txyzb", getline(1))
+
+ " paste (P) unnamed register at the end of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 6)
+ normal P
+ call assert_equal('a xyz b', getline(1))
+
+ " Tests for pasting a register with blockwise mode type
+ call setreg('"', 'xyz', 'b')
+
+ " paste (p) unnamed register at the beginning of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 0)
+ normal p
+ call assert_equal('a xyz b', getline(1))
+
+ " paste (P) unnamed register at the beginning of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 0)
+ normal P
+ call assert_equal("axyz\tb", getline(1))
+
+ " paste (p) unnamed register at the end of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 6)
+ normal p
+ call assert_equal("a\txyzb", getline(1))
+
+ " paste (P) unnamed register at the end of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 6)
+ normal P
+ call assert_equal('a xyz b', getline(1))
+
+ " Tests for pasting with gp and gP in virtual edit mode
+
+ " paste (gp) unnamed register at the beginning of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 0)
+ normal gp
+ call assert_equal('a xyz b', getline(1))
+ call assert_equal([0, 1, 12, 0, 12], getcurpos())
+
+ " paste (gP) unnamed register at the beginning of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 0)
+ normal gP
+ call assert_equal("axyz\tb", getline(1))
+ call assert_equal([0, 1, 5, 0, 5], getcurpos())
+
+ " paste (gp) unnamed register at the end of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 6)
+ normal gp
+ call assert_equal("a\txyzb", getline(1))
+ call assert_equal([0, 1, 6, 0, 12], getcurpos())
+
+ " paste (gP) unnamed register at the end of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 6)
+ normal gP
+ call assert_equal('a xyz b', getline(1))
+ call assert_equal([0, 1, 12, 0, 12], getcurpos())
+
+ " Tests for pasting a named register
+ let @r = 'xyz'
+
+ " paste (gp) named register in the middle of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 2)
+ normal "rgp
+ call assert_equal('a xyz b', getline(1))
+ call assert_equal([0, 1, 8, 0, 8], getcurpos())
+
+ " paste (gP) named register in the middle of a tab
+ call setline(1, "a\tb")
+ call cursor(1, 2, 2)
+ normal "rgP
+ call assert_equal('a xyz b', getline(1))
+ call assert_equal([0, 1, 7, 0, 7], getcurpos())
+
+ bwipe!
+ set virtualedit=
+endfunc
+
+" Test for yanking a few spaces within a tab to a register
+func Test_yank_in_tab()
+ new
+ let @r = ''
+ call setline(1, "a\tb")
+ set virtualedit=all
+ call cursor(1, 2, 2)
+ normal "ry5l
+ call assert_equal(' ', @r)
+
+ bwipe!
+ set virtualedit=
endfunc
" Insert "keyword keyw", ESC, C CTRL-N, shows "keyword ykeyword".
@@ -216,4 +343,139 @@ func Test_yank_paste_small_del_reg()
set virtualedit=
endfunc
+" After calling s:TryVirtualeditReplace(), line 1 will contain one of these
+" two strings, depending on whether virtual editing is on or off.
+let s:result_ve_on = 'a x'
+let s:result_ve_off = 'x'
+
+" Utility function for Test_global_local_virtualedit()
+func s:TryVirtualeditReplace()
+ call setline(1, 'a')
+ normal gg7l
+ normal rx
+endfunc
+
+" Test for :set and :setlocal
+func Test_global_local_virtualedit()
+ new
+
+ " Verify that 'virtualedit' is initialized to empty, can be set globally to
+ " all and to empty, and can be set locally to all and to empty.
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ set ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ set ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ " Verify that :set affects multiple windows.
+ split
+ set ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ wincmd p
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ set ve=
+ wincmd p
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ bwipe!
+
+ " Verify that :setlocal affects only the current window.
+ new
+ split
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ wincmd p
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ bwipe!
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ " Verify that the buffer 'virtualedit' state follows the global value only
+ " when empty and that "none" works as expected.
+ "
+ " 'virtualedit' State
+ " +--------+--------------------------+
+ " | Local | Global |
+ " | | |
+ " +--------+--------+--------+--------+
+ " | | "" | "all" | "none" |
+ " +--------+--------+--------+--------+
+ " | "" | off | on | off |
+ " | "all" | on | on | on |
+ " | "none" | off | off | off |
+ " +--------+--------+--------+--------+
+ new
+
+ setglobal ve=
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=none
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ setglobal ve=all
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=none
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=NONE
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ setglobal ve=none
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=none
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ bwipe!
+
+ " Verify that the 'virtualedit' state is copied to new windows.
+ new
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ split
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ split
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=
+ split
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ bwipe!
+
+ setlocal virtualedit&
+ set virtualedit&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 8344598486..76274fb038 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -433,6 +433,19 @@ func Test_Visual_Block()
close!
endfunc
+" Test for 'p'ut in visual block mode
+func Test_visual_block_put()
+ enew
+
+ call append(0, ['One', 'Two', 'Three'])
+ normal gg
+ yank
+ call feedkeys("jl\<C-V>ljp", 'xt')
+ call assert_equal(['One', 'T', 'Tee', 'One', ''], getline(1, '$'))
+
+ enew!
+endfunc
+
func Test_visual_put_in_block()
new
call setline(1, ['xxxx', 'y∞yy', 'zzzz'])
@@ -1090,6 +1103,13 @@ func Test_visual_put_blockedit_zy_and_zp()
bw!
endfunc
+func Test_visual_block_yank_zy()
+ new
+ " this was reading before the start of the line
+ exe "norm o\<C-T>\<Esc>\<C-V>zy"
+ bwipe!
+endfunc
+
func Test_visual_block_with_virtualedit()
CheckScreendump
@@ -1104,10 +1124,67 @@ func Test_visual_block_with_virtualedit()
call term_sendkeys(buf, "\<C-V>gg$")
call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit', {})
+ call term_sendkeys(buf, "\<Esc>gg\<C-V>G$")
+ call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit2', {})
+
" clean up
call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)
- call delete('XTest_beval')
+ call delete('XTest_block')
+endfunc
+
+func Test_visual_block_ctrl_w_f()
+ " Emtpy block selected in new buffer should not result in an error.
+ au! BufNew foo sil norm f
+ edit foo
+
+ au! BufNew
+endfunc
+
+func Test_visual_reselect_with_count()
+ " this was causing an illegal memory access
+ let lines =<< trim END
+
+
+
+ :
+ r<sfile>
+ exe "%norm e3\<c-v>kr\t"
+ :
+
+ :
+ END
+ call writefile(lines, 'XvisualReselect')
+ source XvisualReselect
+
+ bwipe!
+ call delete('XvisualReselect')
+endfunc
+
+" this was leaving the end of the Visual area beyond the end of a line
+func Test_visual_ex_copy_line()
+ new
+ call setline(1, ["aaa", "bbbbbbbbbxbb"])
+ /x
+ exe "normal ggvjfxO"
+ t0
+ normal gNU
+ bwipe!
+endfunc
+
+" This was leaving the end of the Visual area beyond the end of a line.
+" Set 'undolevels' to start a new undo block.
+func Test_visual_undo_deletes_last_line()
+ new
+ call setline(1, ["aaa", "ccc", "dyd"])
+ set undolevels=100
+ exe "normal obbbbbbbbbxbb\<Esc>"
+ set undolevels=100
+ /y
+ exe "normal ggvjfxO"
+ undo
+ normal gNU
+ bwipe!
endfunc
diff --git a/src/nvim/types.h b/src/nvim/types.h
index 604155c33e..73cd2204d6 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -32,4 +32,6 @@ typedef enum {
kTrue = 1,
} TriState;
+typedef struct Decoration Decoration;
+
#endif // NVIM_TYPES_H
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index d18f35a43a..2d8df4cad8 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2633,6 +2633,10 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
}
}
+ if (VIsual_active) {
+ check_pos(curbuf, &VIsual);
+ }
+
smsg_attr_keep(0,
_("%" PRId64 " %s; %s #%" PRId64 " %s"),
u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index 6c2c136edc..d64d324a88 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -16,8 +16,8 @@ local feed = helpers.feed
local funcs = helpers.funcs
describe('nvim_get_commands', function()
- local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, }
- local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, script_id=0, }
+ local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, keepscript=false, script_id=0, }
+ local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, keepscript=false, script_id=0, }
before_each(clear)
it('gets empty list if no commands were defined', function()
@@ -59,11 +59,11 @@ describe('nvim_get_commands', function()
end)
it('gets various command attributes', function()
- local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, script_id=0, }
- local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, script_id=1, }
- local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, }
- local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, }
- local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, script_id=4, }
+ local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, keepscript=false, script_id=0, }
+ local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, keepscript=false, script_id=1, }
+ local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, keepscript=false, script_id=2, }
+ local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, keepscript=false, script_id=3, }
+ local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, keepscript=false, script_id=4, }
source([[
command -complete=custom,ListUsers -nargs=+ Finger !finger <args>
]])
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index a8f538b951..3f96efd4ef 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -110,6 +110,22 @@ describe('API/extmarks', function()
pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }))
end)
+ it("can end extranges past final newline when strict mode is false", function()
+ set_extmark(ns, marks[1], 0, 0, {
+ end_col = 1,
+ end_row = 1,
+ strict = false,
+ })
+ end)
+
+ it("can end extranges past final column when strict mode is false", function()
+ set_extmark(ns, marks[1], 0, 0, {
+ end_col = 6,
+ end_row = 0,
+ strict = false,
+ })
+ end)
+
it('adds, updates and deletes marks', function()
local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
eq(marks[1], rv)
@@ -1430,7 +1446,60 @@ describe('API/extmarks', function()
end_col = 0,
end_line = 1
})
- eq({ {1, 0, 0, { end_col = 0, end_row = 1 }} }, get_extmarks(ns, 0, -1, {details=true}))
+ eq({ {1, 0, 0, {
+ end_col = 0,
+ end_row = 1,
+ right_gravity = true,
+ end_right_gravity = false,
+ }} }, get_extmarks(ns, 0, -1, {details=true}))
+ end)
+
+ it('can get details', function()
+ set_extmark(ns, marks[1], 0, 0, {
+ end_col = 0,
+ end_row = 1,
+ right_gravity = false,
+ end_right_gravity = true,
+ priority = 0,
+ hl_eol = true,
+ hl_mode = "blend",
+ hl_group = "String",
+ virt_text = { { "text", "Statement" } },
+ virt_text_pos = "right_align",
+ virt_text_hide = true,
+ virt_lines = { { { "lines", "Statement" } }},
+ virt_lines_above = true,
+ virt_lines_leftcol = true,
+ })
+ set_extmark(ns, marks[2], 0, 0, {
+ priority = 0,
+ virt_text = { { "text", "Statement" } },
+ virt_text_win_col = 1,
+ })
+ eq({0, 0, {
+ end_col = 0,
+ end_row = 1,
+ right_gravity = false,
+ end_right_gravity = true,
+ priority = 0,
+ hl_eol = true,
+ hl_mode = "blend",
+ hl_group = "String",
+ virt_text = { { "text", "Statement" } },
+ virt_text_pos = "right_align",
+ virt_text_hide = true,
+ virt_lines = { { { "lines", "Statement" } }},
+ virt_lines_above = true,
+ virt_lines_leftcol = true,
+ } }, get_extmark_by_id(ns, marks[1], { details = true }))
+ eq({0, 0, {
+ right_gravity = true,
+ priority = 0,
+ virt_text = { { "text", "Statement" } },
+ virt_text_hide = false,
+ virt_text_pos = "win_col",
+ virt_text_win_col = 1,
+ } }, get_extmark_by_id(ns, marks[2], { details = true }))
end)
end)
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index bc2759ade7..450a76ddac 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -820,6 +820,16 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq(99, exec_lua[[return SomeValue]])
end)
+ it('does not reset pum in lua mapping', function()
+ eq(0, exec_lua [[
+ VisibleCount = 0
+ vim.api.nvim_set_keymap ('i', '<F2>', '', {callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end})
+ return VisibleCount
+ ]])
+ feed('i<C-X><C-V><F2><F2><esc>')
+ eq(2, exec_lua[[return VisibleCount]])
+ end)
+
it('can overwrite lua mappings', function()
eq(0, exec_lua [[
GlobalCount = 0
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 22201e21a2..201ba45803 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -1367,18 +1367,18 @@ describe('API', function()
end)
describe('nvim_feedkeys', function()
- it('CSI escaping', function()
+ it('K_SPECIAL escaping', function()
local function on_setup()
-- notice the special char(…) \xe2\80\xa6
nvim('feedkeys', ':let x1="…"\n', '', true)
-- Both nvim_replace_termcodes and nvim_feedkeys escape \x80
local inp = helpers.nvim('replace_termcodes', ':let x2="…"<CR>', true, true, true)
- nvim('feedkeys', inp, '', true) -- escape_csi=true
+ nvim('feedkeys', inp, '', true) -- escape_ks=true
- -- nvim_feedkeys with CSI escaping disabled
+ -- nvim_feedkeys with K_SPECIAL escaping disabled
inp = helpers.nvim('replace_termcodes', ':let x3="…"<CR>', true, true, true)
- nvim('feedkeys', inp, '', false) -- escape_csi=false
+ nvim('feedkeys', inp, '', false) -- escape_ks=false
helpers.stop()
end
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua
index 93dec9fb35..c28300f0f4 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -100,6 +100,38 @@ describe('channels', function()
eq({"notification", "exit", {3,0}}, next_msg())
end)
+ it('can use stdio channel and on_print callback', function()
+ source([[
+ let g:job_opts = {
+ \ 'on_stdout': function('OnEvent'),
+ \ 'on_stderr': function('OnEvent'),
+ \ 'on_exit': function('OnEvent'),
+ \ }
+ ]])
+ meths.set_var("nvim_prog", nvim_prog)
+ meths.set_var("code", [[
+ function! OnStdin(id, data, event) dict
+ echo string([a:id, a:data, a:event])
+ if a:data == ['']
+ quit
+ endif
+ endfunction
+ function! OnPrint(text) dict
+ call chansend(g:x, ['OnPrint:' .. a:text])
+ endfunction
+ let g:x = stdioopen({'on_stdin': funcref('OnStdin'), 'on_print':'OnPrint'})
+ call chansend(x, "hello")
+ ]])
+ command("let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)")
+ local id = eval("g:id")
+ ok(id > 0)
+
+ eq({ "notification", "stdout", {id, { "hello" } } }, next_msg())
+
+ command("call chansend(id, 'howdy')")
+ eq({"notification", "stdout", {id, {"OnPrint:[1, ['howdy'], 'stdin']"}}}, next_msg())
+ end)
+
local function expect_twoline(id, stream, line1, line2, nobr)
local msg = next_msg()
local joined = nobr and {line1..line2} or {line1, line2}
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index 4220d68ee1..2fa84e8313 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -224,6 +224,23 @@ describe('startup', function()
end
end)
+ it('-e sets ex mode', function()
+ local screen = Screen.new(25, 3)
+ clear('-e')
+ screen:attach()
+ -- Verify we set the proper mode both before and after :vi.
+ feed("put =mode(1)<CR>vi<CR>:put =mode(1)<CR>")
+ screen:expect([[
+ cv |
+ ^n |
+ :put =mode(1) |
+ ]])
+
+ eq('cv\n',
+ funcs.system({nvim_prog, '-n', '-es' },
+ { 'put =mode(1)', 'print', '' }))
+ end)
+
it('fails on --embed with -es/-Es', function()
matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es',
funcs.system({nvim_prog, '--embed', '-es' }))
@@ -651,11 +668,7 @@ describe('runtime:', function()
mkdir_p(ftdetect_folder)
write_file(ftdetect_file , [[vim.g.lua_ftdetect = 1]])
- -- TODO(shadmansaleh): Figure out why this test fails without
- -- setting VIMRUNTIME
- clear{ args_rm={'-u'}, env={XDG_CONFIG_HOME=xconfig,
- XDG_DATA_HOME=xdata,
- VIMRUNTIME='runtime/'}}
+ clear{ args_rm={'-u'}, env=xenv }
eq(1, eval('g:lua_ftdetect'))
rmdir(ftdetect_folder)
diff --git a/test/functional/editor/K_spec.lua b/test/functional/editor/K_spec.lua
index 40f36491e4..8ad81ac3d6 100644
--- a/test/functional/editor/K_spec.lua
+++ b/test/functional/editor/K_spec.lua
@@ -33,6 +33,29 @@ describe('K', function()
feed('i'..test_file..'<ESC>K')
retry(nil, nil, function() eq(1, eval('filereadable("'..test_file..'")')) end)
eq({'fnord'}, eval("readfile('"..test_file.."')"))
+ -- Confirm that Neovim is still in terminal mode after K is pressed (#16692).
+ helpers.sleep(500)
+ eq('t', eval('mode()'))
+ feed('<space>') -- Any key, not just <space>, can be used here to escape.
+ eq('n', eval('mode()'))
+ end)
+
+ it("<esc> kills the buffer for a running 'keywordprg' command", function()
+ helpers.source('set keywordprg=less')
+ eval('writefile(["hello", "world"], "' .. test_file .. '")')
+ feed('i' .. test_file .. '<esc>K')
+ eq('t', eval('mode()'))
+ -- Confirm that an arbitrary keypress doesn't escape (i.e., the process is
+ -- still running). If the process were no longer running, an arbitrary
+ -- keypress would escape.
+ helpers.sleep(500)
+ feed('<space>')
+ eq('t', eval('mode()'))
+ -- Confirm that <esc> kills the buffer for the running command.
+ local bufnr = eval('bufnr()')
+ feed('<esc>')
+ eq('n', eval('mode()'))
+ helpers.neq(bufnr, eval('bufnr()'))
end)
end)
diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua
index 2280f5bb24..f811b8ae8d 100644
--- a/test/functional/editor/meta_key_spec.lua
+++ b/test/functional/editor/meta_key_spec.lua
@@ -27,6 +27,14 @@ describe('meta-keys #8226 #13042', function()
command('nnoremap <A-j> Aalt-j<Esc>')
feed('<A-j><M-l>')
expect('llo<ESC>;<ESC>;alt-jmeta-l')
+ -- Unmapped ALT-chord with characters containing K_SPECIAL bytes
+ command('nnoremap … A…<Esc>')
+ feed('<A-…><M-…>')
+ expect('llo<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…')
+ command("execute 'nnoremap' nr2char(0x40000000) 'AMAX<Esc>'")
+ command("call nvim_input('<A-'.nr2char(0x40000000).'>')")
+ command("call nvim_input('<M-'.nr2char(0x40000000).'>')")
+ expect('llo<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…<ESC>MAX<ESC>MAX')
end)
it('ALT/META, visual-mode', function()
@@ -36,13 +44,20 @@ describe('meta-keys #8226 #13042', function()
expect('peach')
-- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869
command('vnoremap <ESC> A<lt>ESC>')
- feed('viw<A-;><ESC>viw<M-;><ESC>')
+ feed('viw<A-;><Esc>viw<M-;><Esc>')
expect('peach<ESC>;<ESC>;')
-- Mapped ALT-chord behaves as mapped.
command('vnoremap <M-l> Ameta-l<Esc>')
command('vnoremap <A-j> Aalt-j<Esc>')
feed('viw<A-j>viw<M-l>')
expect('peach<ESC>;<ESC>;alt-jmeta-l')
+ -- Unmapped ALT-chord with characters containing K_SPECIAL bytes
+ feed('viw<A-…><Esc>viw<M-…><Esc>')
+ expect('peach<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…')
+ command("execute 'inoremap' nr2char(0x40000000) 'MAX'")
+ command("call nvim_input('viw<A-'.nr2char(0x40000000).'><Esc>')")
+ command("call nvim_input('viw<M-'.nr2char(0x40000000).'><Esc>')")
+ expect('peach<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…<ESC>MAX<ESC>MAX')
end)
it('ALT/META insert-mode', function()
diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua
index f03508035d..528e228121 100644
--- a/test/functional/editor/mode_insert_spec.lua
+++ b/test/functional/editor/mode_insert_spec.lua
@@ -75,4 +75,57 @@ describe('insert-mode', function()
expect('hello oooworld')
end)
end)
+
+ describe('Ctrl-V', function()
+ it('supports entering the decimal value of a character', function()
+ feed('i<C-V>076<C-V>167')
+ expect('L§')
+ end)
+
+ it('supports entering the octal value of a character with "o"', function()
+ feed('i<C-V>o114<C-V>o247<Esc>')
+ expect('L§')
+ end)
+
+ it('supports entering the octal value of a character with "O"', function()
+ feed('i<C-V>O114<C-V>O247<Esc>')
+ expect('L§')
+ end)
+
+ it('supports entering the hexadecimal value of a character with "x"', function()
+ feed('i<C-V>x4c<C-V>xA7<Esc>')
+ expect('L§')
+ end)
+
+ it('supports entering the hexadecimal value of a character with "X"', function()
+ feed('i<C-V>X4c<C-V>XA7<Esc>')
+ expect('L§')
+ end)
+
+ it('supports entering the hexadecimal value of a character with "u"', function()
+ feed('i<C-V>u25ba<C-V>u25C7<Esc>')
+ expect('►◇')
+ end)
+
+ it('supports entering the hexadecimal value of a character with "U"', function()
+ feed('i<C-V>U0001f600<C-V>U0001F601<Esc>')
+ expect('😀😁')
+ end)
+
+ it('entering character by value is interrupted by invalid character', function()
+ feed('i<C-V>76c<C-V>76<C-F2><C-V>u3c0j<C-V>u3c0<M-F3><C-V>U1f600j<C-V>U1f600<D-F4><Esc>')
+ expect('LcL<C-F2>πjπ<M-F3>😀j😀<D-F4>')
+ end)
+
+ it('shows o, O, u, U, x, X, and digits with modifiers', function()
+ feed('i<C-V><M-o><C-V><D-o><C-V><M-O><C-V><D-O><Esc>')
+ expect('<M-o><D-o><M-O><D-O>')
+ feed('cc<C-V><M-u><C-V><D-u><C-V><M-U><C-V><D-U><Esc>')
+ expect('<M-u><D-u><M-U><D-U>')
+ feed('cc<C-V><M-x><C-V><D-x><C-V><M-X><C-V><D-X><Esc>')
+ expect('<M-x><D-x><M-X><D-X>')
+ feed('cc<C-V><M-1><C-V><D-2><C-V><M-7><C-V><D-8><Esc>')
+ expect('<M-1><D-2><M-7><D-8>')
+ end)
+ end)
end)
diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua
index 26967ecbba..fdda2be131 100644
--- a/test/functional/editor/put_spec.lua
+++ b/test/functional/editor/put_spec.lua
@@ -64,7 +64,7 @@ describe('put command', function()
-- one place to the right (unless we were at the end of the
-- line when we pasted).
if not (exception_table.undo_position and after_undo) then
- eq(funcs.getcurpos(), init_cursorpos)
+ eq(init_cursorpos, funcs.getcurpos())
end
end
@@ -86,7 +86,7 @@ describe('put command', function()
-- If we paste the ". register with a count we can't avoid
-- changing this register, hence avoid this check.
if not test.exception_table.dot_reg_changed then
- eq(funcs.getreg('.'), orig_dotstr)
+ eq(orig_dotstr, funcs.getreg('.'))
end
-- Doing something, undoing it, and then redoing it should
@@ -507,9 +507,9 @@ describe('put command', function()
return function(exception_table, after_redo)
test_expect(exception_table, after_redo)
if selection_string then
- eq(getreg('"'), selection_string)
+ eq(selection_string, getreg('"'))
else
- eq(getreg('"'), 'test_string"')
+ eq('test_string"', getreg('"'))
end
end
end
@@ -714,7 +714,7 @@ describe('put command', function()
expect_base, conversion_table)
return function(exception_table, after_redo)
test_expect(exception_table, after_redo)
- eq(getreg('"'), 'Line of words 1\n')
+ eq('Line of words 1\n', getreg('"'))
end
end
local base_expect_string = [[
@@ -748,7 +748,7 @@ describe('put command', function()
end, expect_base, conversion_table)
return function(e,c)
test_expect(e,c)
- eq(getreg('"'), 'Lin\nLin')
+ eq('Lin\nLin', getreg('"'))
end
end
@@ -800,9 +800,9 @@ describe('put command', function()
feed('u')
-- Have to use feed('u') here to set curswant, because
-- ex_undo() doesn't do that.
- eq(funcs.getcurpos(), {0, 1, 1, 0, 1})
+ eq({0, 1, 1, 0, 1}, funcs.getcurpos())
feed('<C-r>')
- eq(funcs.getcurpos(), {0, 1, 1, 0, 1})
+ eq({0, 1, 1, 0, 1}, funcs.getcurpos())
end
end
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index f152a487af..1845786c4b 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -451,6 +451,7 @@ function module.new_argv(...)
'GCOV_ERROR_FILE',
'XDG_DATA_DIRS',
'TMPDIR',
+ 'VIMRUNTIME',
}) do
if not env_tbl[k] then
env_tbl[k] = os.getenv(k)
diff --git a/test/functional/legacy/listchars_spec.lua b/test/functional/legacy/listchars_spec.lua
index dc6ccd3628..7a1afa1fd6 100644
--- a/test/functional/legacy/listchars_spec.lua
+++ b/test/functional/legacy/listchars_spec.lua
@@ -1,7 +1,8 @@
-- Tests for 'listchars' display with 'list' and :list.
local helpers = require('test.functional.helpers')(after_each)
-local feed, insert, source = helpers.feed, helpers.insert, helpers.source
+local Screen = require('test.functional.ui.screen')
+local feed, insert, exec = helpers.feed, helpers.insert, helpers.exec
local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect
-- luacheck: ignore 621 (Indentation)
@@ -13,7 +14,7 @@ describe("'listchars'", function()
-- luacheck: ignore 613 (Trailing whitespace in a string)
it("works with 'list'", function()
- source([[
+ exec([[
function GetScreenCharsForLine(lnum)
return join(map(range(1, virtcol('$')), 'nr2char(screenchar(a:lnum, v:val))'), '')
endfunction
@@ -98,4 +99,80 @@ describe("'listchars'", function()
.....h>-$
iii<<<<><<$]])
end)
+
+ it('"exceeds" character does not appear in foldcolumn vim-patch:8.2.3121', function()
+ local screen = Screen.new(60, 10)
+ screen:attach()
+ exec([[
+ call setline(1, ['aaa', '', 'a', 'aaaaaa'])
+ vsplit
+ vsplit
+ windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:<
+ ]])
+ feed('13<C-W>>')
+ screen:expect([[
+ aaa │ a>│ ^aaa |
+ │ │ |
+ a │ a │ a |
+ aaaaaa │ a>│ aaaaaa |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ [No Name] [+] <[+] [No Name] [+] |
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ aaa │ >│ ^aaa |
+ │ │ |
+ a │ a│ a |
+ aaaaaa │ >│ aaaaaa |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ [No Name] [+] <+] [No Name] [+] |
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ aaa │ │ ^aaa |
+ │ │ |
+ a │ │ a |
+ aaaaaa │ │ aaaaaa |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ [No Name] [+] <] [No Name] [+] |
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ aaa │ │ ^aaa |
+ │ │ |
+ a │ │ a |
+ aaaaaa │ │ aaaaaa |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ ~ │~ │~ |
+ [No Name] [+] < [No Name] [+] |
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ aaa │ │ ^aaa |
+ │ │ |
+ a │ │ a |
+ aaaaaa │ │ aaaaaa |
+ ~ │~│~ |
+ ~ │~│~ |
+ ~ │~│~ |
+ ~ │~│~ |
+ [No Name] [+] < [No Name] [+] |
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/visual_mode_spec.lua b/test/functional/legacy/visual_mode_spec.lua
index c8e83ed649..8b5dd0c2dc 100644
--- a/test/functional/legacy/visual_mode_spec.lua
+++ b/test/functional/legacy/visual_mode_spec.lua
@@ -1,5 +1,3 @@
--- Test visual line mode selection redraw after scrolling
-
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
@@ -10,6 +8,7 @@ local feed_command = helpers.feed_command
local funcs = helpers.funcs
local meths = helpers.meths
local eq = helpers.eq
+local exec = helpers.exec
describe('visual line mode', function()
local screen
@@ -40,3 +39,44 @@ describe('visual line mode', function()
]])
end)
end)
+
+describe('visual block mode', function()
+ it('shows selection correctly with virtualedit=block', function()
+ clear()
+ local screen = Screen.new(30, 7)
+ screen:set_default_attr_ids({
+ [1] = {bold = true}, -- ModeMsg
+ [2] = {background = Screen.colors.LightGrey}, -- Visual
+ [3] = {foreground = Screen.colors.Blue, bold = true} -- NonText
+ })
+ screen:attach()
+
+ exec([[
+ call setline(1, ['aaaaaa', 'bbbb', 'cc'])
+ set virtualedit=block
+ normal G
+ ]])
+
+ feed('<C-V>gg$')
+ screen:expect([[
+ {2:aaaaaa}^ |
+ {2:bbbb } |
+ {2:cc } |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {1:-- VISUAL BLOCK --} |
+ ]])
+
+ feed('<Esc>gg<C-V>G$')
+ screen:expect([[
+ {2:aaaaaa } |
+ {2:bbbb } |
+ {2:cc}^ {2: } |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {1:-- VISUAL BLOCK --} |
+ ]])
+ end)
+end)
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index 7d260f2e29..b58fad1cab 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -208,10 +208,10 @@ describe('vim.diagnostic', function()
eq(all_highlights, exec_lua [[
local ns_1_diags = {
make_error("Error 1", 1, 1, 1, 5),
- make_warning("Warning on Server 1", 2, 1, 2, 5),
+ make_warning("Warning on Server 1", 2, 1, 2, 3),
}
local ns_2_diags = {
- make_warning("Warning 1", 2, 1, 2, 5),
+ make_warning("Warning 1", 2, 1, 2, 3),
}
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags)
@@ -255,10 +255,10 @@ describe('vim.diagnostic', function()
eq({0, 2}, exec_lua [[
local ns_1_diags = {
make_error("Error 1", 1, 1, 1, 5),
- make_warning("Warning on Server 1", 2, 1, 2, 5),
+ make_warning("Warning on Server 1", 2, 1, 2, 3),
}
local ns_2_diags = {
- make_warning("Warning 1", 2, 1, 2, 5),
+ make_warning("Warning 1", 2, 1, 2, 3),
}
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags)
@@ -599,10 +599,10 @@ describe('vim.diagnostic', function()
eq(all_highlights, exec_lua [[
local ns_1_diags = {
make_error("Error 1", 1, 1, 1, 5),
- make_warning("Warning on Server 1", 2, 1, 2, 5),
+ make_warning("Warning on Server 1", 2, 1, 2, 3),
}
local ns_2_diags = {
- make_warning("Warning 1", 2, 1, 2, 5),
+ make_warning("Warning 1", 2, 1, 2, 3),
}
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags)
@@ -787,7 +787,7 @@ describe('vim.diagnostic', function()
eq(2, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error("Error 1", 1, 1, 1, 5),
- make_warning("Warning on Server 1", 1, 1, 2, 5),
+ make_warning("Warning on Server 1", 1, 1, 2, 3),
})
return #vim.diagnostic.get(diagnostic_bufnr)
@@ -798,9 +798,9 @@ describe('vim.diagnostic', function()
eq({2, 3, 2}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error("Error 1", 1, 1, 1, 5),
- make_warning("Warning on Server 1", 1, 1, 2, 5),
- make_info("Ignored information", 1, 1, 2, 5),
- make_hint("Here's a hint", 1, 1, 2, 5),
+ make_warning("Warning on Server 1", 1, 1, 2, 3),
+ make_info("Ignored information", 1, 1, 2, 3),
+ make_hint("Here's a hint", 1, 1, 2, 3),
})
return {
@@ -820,8 +820,8 @@ describe('vim.diagnostic', function()
eq(1, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error("Error 1", 1, 1, 1, 5),
- make_warning("Warning on Server 1", 1, 1, 2, 5),
- make_info("Ignored information", 1, 1, 2, 5),
+ make_warning("Warning on Server 1", 1, 1, 2, 3),
+ make_info("Ignored information", 1, 1, 2, 3),
make_error("Error On Other Line", 2, 1, 1, 5),
})
diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua
index 756591571e..729ebc601b 100644
--- a/test/functional/lua/filetype_spec.lua
+++ b/test/functional/lua/filetype_spec.lua
@@ -3,9 +3,6 @@ local exec_lua = helpers.exec_lua
local eq = helpers.eq
local clear = helpers.clear
local pathroot = helpers.pathroot
-local meths = helpers.meths
-local curbufmeths = helpers.curbufmeths
-local funcs = helpers.funcs
local root = pathroot()
@@ -75,10 +72,10 @@ describe('vim.filetype', function()
local root = ...
vim.filetype.add({
pattern = {
- [root .. '/blog/.*%.txt'] = 'markdown',
+ ['~/blog/.*%.txt'] = 'markdown',
}
})
- vim.filetype.match(root .. '/blog/why_neovim_is_awesome.txt')
+ vim.filetype.match('~/blog/why_neovim_is_awesome.txt')
return vim.bo.filetype
]], root))
end)
@@ -98,572 +95,4 @@ describe('vim.filetype', function()
return vim.bo.filetype
]])
end)
-
- it('correctly detects filetypes from test_filetype', function()
- -- Checks copied from test_filetype.vim
- local filename_checks = {
- ['8th'] = {'file.8th'},
- ['a2ps'] = {'/etc/a2ps.cfg', '/etc/a2ps/file.cfg', 'a2psrc', '.a2psrc', 'any/etc/a2ps.cfg', 'any/etc/a2ps/file.cfg'},
- ['a65'] = {'file.a65'},
- ['aap'] = {'file.aap'},
- ['abap'] = {'file.abap'},
- ['abc'] = {'file.abc'},
- ['abel'] = {'file.abl'},
- ['acedb'] = {'file.wrm'},
- ['ada'] = {'file.adb', 'file.ads', 'file.ada', 'file.gpr'},
- ['ahdl'] = {'file.tdf'},
- ['aidl'] = {'file.aidl'},
- ['alsaconf'] = {'.asoundrc', '/usr/share/alsa/alsa.conf', '/etc/asound.conf', 'any/etc/asound.conf', 'any/usr/share/alsa/alsa.conf'},
- ['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', '/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'},
- ['arch'] = {'.arch-inventory', '=tagging-method'},
- ['arduino'] = {'file.ino', 'file.pde'},
- ['art'] = {'file.art'},
- ['asciidoc'] = {'file.asciidoc', 'file.adoc'},
- ['asn'] = {'file.asn', 'file.asn1'},
- ['asterisk'] = {'asterisk/file.conf', 'asterisk/file.conf-file', 'some-asterisk/file.conf', 'some-asterisk/file.conf-file'},
- ['atlas'] = {'file.atl', 'file.as'},
- ['autohotkey'] = {'file.ahk'},
- ['autoit'] = {'file.au3'},
- ['automake'] = {'GNUmakefile.am', 'makefile.am', 'Makefile.am'},
- ['ave'] = {'file.ave'},
- ['awk'] = {'file.awk', 'file.gawk'},
- ['b'] = {'file.mch', 'file.ref', 'file.imp'},
- ['bzl'] = {'file.bazel', 'file.bzl', 'WORKSPACE'},
- ['bc'] = {'file.bc'},
- ['bdf'] = {'file.bdf'},
- ['bib'] = {'file.bib'},
- ['beancount'] = {'file.beancount'},
- ['bindzone'] = {'named.root', '/bind/db.file', '/named/db.file', 'any/bind/db.file', 'any/named/db.file'},
- ['blank'] = {'file.bl'},
- ['bsdl'] = {'file.bsd', 'file.bsdl', 'bsd', 'some-bsd'},
- ['bst'] = {'file.bst'},
- ['bzr'] = {'bzr_log.any', 'bzr_log.file'},
- ['c'] = {'enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg'},
- ['cabal'] = {'file.cabal'},
- ['cabalconfig'] = {'cabal.config'},
- ['cabalproject'] = {'cabal.project', 'cabal.project.local'},
- ['calendar'] = {'calendar', '/.calendar/file', '/share/calendar/any/calendar.file', '/share/calendar/calendar.file', 'any/share/calendar/any/calendar.file', 'any/share/calendar/calendar.file'},
- ['catalog'] = {'catalog', 'sgml.catalogfile', 'sgml.catalog', 'sgml.catalog-file'},
- ['cdl'] = {'file.cdl'},
- ['cdrdaoconf'] = {'/etc/cdrdao.conf', '/etc/defaults/cdrdao', '/etc/default/cdrdao', '.cdrdao', 'any/etc/cdrdao.conf', 'any/etc/default/cdrdao', 'any/etc/defaults/cdrdao'},
- ['cdrtoc'] = {'file.toc'},
- ['cf'] = {'file.cfm', 'file.cfi', 'file.cfc'},
- ['cfengine'] = {'cfengine.conf'},
- ['cfg'] = {'file.cfg', 'file.hgrc', 'filehgrc', 'hgrc', 'some-hgrc'},
- ['ch'] = {'file.chf'},
- ['chaiscript'] = {'file.chai'},
- ['chaskell'] = {'file.chs'},
- ['chill'] = {'file..ch'},
- ['chordpro'] = {'file.chopro', 'file.crd', 'file.cho', 'file.crdpro', 'file.chordpro'},
- ['cl'] = {'file.eni'},
- ['clean'] = {'file.dcl', 'file.icl'},
- ['clojure'] = {'file.clj', 'file.cljs', 'file.cljx', 'file.cljc'},
- ['cmake'] = {'CMakeLists.txt', 'file.cmake', 'file.cmake.in'},
- ['cmusrc'] = {'any/.cmus/autosave', 'any/.cmus/rc', 'any/.cmus/command-history', 'any/.cmus/file.theme', 'any/cmus/rc', 'any/cmus/file.theme', '/.cmus/autosave', '/.cmus/command-history', '/.cmus/file.theme', '/.cmus/rc', '/cmus/file.theme', '/cmus/rc'},
- ['cobol'] = {'file.cbl', 'file.cob', 'file.lib'},
- ['coco'] = {'file.atg'},
- ['conaryrecipe'] = {'file.recipe'},
- ['conf'] = {'auto.master'},
- ['config'] = {'configure.in', 'configure.ac', '/etc/hostname.file'},
- ['context'] = {'tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'},
- ['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', 'file.csx'},
- ['csc'] = {'file.csc'},
- ['csdl'] = {'file.csdl'},
- ['csp'] = {'file.csp', 'file.fdr'},
- ['css'] = {'file.css'},
- ['cterm'] = {'file.con'},
- ['cucumber'] = {'file.feature'},
- ['cuda'] = {'file.cu', 'file.cuh'},
- ['cupl'] = {'file.pld'},
- ['cuplsim'] = {'file.si'},
- ['cvs'] = {'cvs123'},
- ['cvsrc'] = {'.cvsrc'},
- ['cynpp'] = {'file.cyn'},
- ['dart'] = {'file.dart', 'file.drt'},
- ['datascript'] = {'file.ds'},
- ['dcd'] = {'file.dcd'},
- ['debchangelog'] = {'changelog.Debian', 'changelog.dch', 'NEWS.Debian', 'NEWS.dch', '/debian/changelog'},
- ['debcontrol'] = {'/debian/control', 'any/debian/control'},
- ['debcopyright'] = {'/debian/copyright', 'any/debian/copyright'},
- ['debsources'] = {'/etc/apt/sources.list', '/etc/apt/sources.list.d/file.list', 'any/etc/apt/sources.list', 'any/etc/apt/sources.list.d/file.list'},
- ['def'] = {'file.def'},
- ['denyhosts'] = {'denyhosts.conf'},
- ['desc'] = {'file.desc'},
- ['desktop'] = {'file.desktop', '.directory', 'file.directory'},
- ['dictconf'] = {'dict.conf', '.dictrc'},
- ['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'},
- ['dockerfile'] = {'Containerfile', 'Dockerfile', 'file.Dockerfile', 'Dockerfile.debian', 'Containerfile.something'},
- ['dosbatch'] = {'file.bat', 'file.sys'},
- ['dosini'] = {'.editorconfig', '/etc/pacman.conf', '/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/pacman.conf', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap'},
- ['dot'] = {'file.dot', 'file.gv'},
- ['dracula'] = {'file.drac', 'file.drc', 'filelvs', 'filelpe', 'drac.file', 'lpe', 'lvs', 'some-lpe', 'some-lvs'},
- ['dtd'] = {'file.dtd'},
- ['dts'] = {'file.dts', 'file.dtsi'},
- ['dune'] = {'jbuild', 'dune', 'dune-project', 'dune-workspace'},
- ['dylan'] = {'file.dylan'},
- ['dylanintr'] = {'file.intr'},
- ['dylanlid'] = {'file.lid'},
- ['ecd'] = {'file.ecd'},
- ['edif'] = {'file.edf', 'file.edif', 'file.edo'},
- ['elinks'] = {'elinks.conf'},
- ['elixir'] = {'file.ex', 'file.exs', 'mix.lock'},
- ['eelixir'] = {'file.eex', 'file.leex'},
- ['elm'] = {'file.elm'},
- ['elmfilt'] = {'filter-rules'},
- ['epuppet'] = {'file.epp'},
- ['erlang'] = {'file.erl', 'file.hrl', 'file.yaws'},
- ['eruby'] = {'file.erb', 'file.rhtml'},
- ['esmtprc'] = {'anyesmtprc', 'esmtprc', 'some-esmtprc'},
- ['esqlc'] = {'file.ec', 'file.EC'},
- ['esterel'] = {'file.strl'},
- ['eterm'] = {'anyEterm/file.cfg', 'Eterm/file.cfg', 'some-Eterm/file.cfg'},
- ['exim'] = {'exim.conf'},
- ['expect'] = {'file.exp'},
- ['exports'] = {'exports'},
- ['factor'] = {'file.factor'},
- ['falcon'] = {'file.fal'},
- ['fan'] = {'file.fan', 'file.fwt'},
- ['fennel'] = {'file.fnl'},
- ['fetchmail'] = {'.fetchmailrc'},
- ['fgl'] = {'file.4gl', 'file.4gh', 'file.m4gl'},
- ['fish'] = {'file.fish'},
- ['focexec'] = {'file.fex', 'file.focexec'},
- ['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', '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'},
- ['gift'] = {'file.gift'},
- ['gitcommit'] = {'COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'},
- ['gitconfig'] = {'file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'},
- ['gitolite'] = {'gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'},
- ['gitrebase'] = {'git-rebase-todo'},
- ['gitsendemail'] = {'.gitsendemail.msg.xxxxxx'},
- ['gkrellmrc'] = {'gkrellmrc', 'gkrellmrc_x'},
- ['gnash'] = {'gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'},
- ['gnuplot'] = {'file.gpi'},
- ['go'] = {'file.go'},
- ['gomod'] = {'go.mod'},
- ['gp'] = {'file.gp', '.gprc'},
- ['gpg'] = {'/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'},
- ['grads'] = {'file.gs'},
- ['gretl'] = {'file.gretl'},
- ['groovy'] = {'file.gradle', 'file.groovy'},
- ['group'] = {'any/etc/group', 'any/etc/group-', 'any/etc/group.edit', 'any/etc/gshadow', 'any/etc/gshadow-', 'any/etc/gshadow.edit', 'any/var/backups/group.bak', 'any/var/backups/gshadow.bak', '/etc/group', '/etc/group-', '/etc/group.edit', '/etc/gshadow', '/etc/gshadow-', '/etc/gshadow.edit', '/var/backups/group.bak', '/var/backups/gshadow.bak'},
- ['grub'] = {'/boot/grub/menu.lst', '/boot/grub/grub.conf', '/etc/grub.conf', 'any/boot/grub/grub.conf', 'any/boot/grub/menu.lst', 'any/etc/grub.conf'},
- ['gsp'] = {'file.gsp'},
- ['gtkrc'] = {'.gtkrc', 'gtkrc', '.gtkrc-file', 'gtkrc-file'},
- ['haml'] = {'file.haml'},
- ['hamster'] = {'file.hsm'},
- ['haskell'] = {'file.hs', 'file.hsc', 'file.hs-boot', 'file.hsig'},
- ['haste'] = {'file.ht'},
- ['hastepreproc'] = {'file.htpp'},
- ['hb'] = {'file.hb'},
- ['hercules'] = {'file.vc', 'file.ev', 'file.sum', 'file.errsum'},
- ['hex'] = {'file.hex', 'file.h32'},
- ['hgcommit'] = {'hg-editor-file.txt'},
- ['hog'] = {'file.hog', 'snort.conf', 'vision.conf'},
- ['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'},
- ['neomuttrc'] = {'Neomuttrc', '.neomuttrc', '.neomuttrc-file', '/.neomutt/neomuttrc', '/.neomutt/neomuttrc-file', 'Neomuttrc', 'Neomuttrc-file', 'any/.neomutt/neomuttrc', 'any/.neomutt/neomuttrc-file', 'neomuttrc', 'neomuttrc-file'},
- ['opl'] = {'file.OPL', 'file.OPl', 'file.OpL', 'file.Opl', 'file.oPL', 'file.oPl', 'file.opL', 'file.opl'},
- ['pcmk'] = {'file.pcmk'},
- ['r'] = {'file.r'},
- ['rhelp'] = {'file.rd'},
- ['rmd'] = {'file.rmd', 'file.smd'},
- ['rnoweb'] = {'file.rnw', 'file.snw'},
- ['rrst'] = {'file.rrst', 'file.srst'},
- ['template'] = {'file.tmpl'},
- ['htmlm4'] = {'file.html.m4'},
- ['httest'] = {'file.htt', 'file.htb'},
- ['ibasic'] = {'file.iba', 'file.ibi'},
- ['icemenu'] = {'/.icewm/menu', 'any/.icewm/menu'},
- ['icon'] = {'file.icn'},
- ['indent'] = {'.indent.pro', 'indentrc'},
- ['inform'] = {'file.inf', 'file.INF'},
- ['initng'] = {'/etc/initng/any/file.i', 'file.ii', 'any/etc/initng/any/file.i'},
- ['inittab'] = {'inittab'},
- ['ipfilter'] = {'ipf.conf', 'ipf6.conf', 'ipf.rules'},
- ['iss'] = {'file.iss'},
- ['ist'] = {'file.ist', 'file.mst'},
- ['j'] = {'file.ijs'},
- ['jal'] = {'file.jal', 'file.JAL'},
- ['jam'] = {'file.jpl', 'file.jpr', 'JAM-file.file', 'JAM.file', 'Prl-file.file', 'Prl.file'},
- ['java'] = {'file.java', 'file.jav'},
- ['javacc'] = {'file.jj', 'file.jjt'},
- ['javascript'] = {'file.js', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'},
- ['javascriptreact'] = {'file.jsx'},
- ['jess'] = {'file.clp'},
- ['jgraph'] = {'file.jgr'},
- ['jovial'] = {'file.jov', 'file.j73', 'file.jovial'},
- ['jproperties'] = {'file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'},
- ['json'] = {'file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'},
- ['jsonc'] = {'file.jsonc'},
- ['jsp'] = {'file.jsp'},
- ['julia'] = {'file.jl'},
- ['kconfig'] = {'Kconfig', 'Kconfig.debug', 'Kconfig.file'},
- ['kivy'] = {'file.kv'},
- ['kix'] = {'file.kix'},
- ['kotlin'] = {'file.kt', 'file.ktm', 'file.kts'},
- ['kscript'] = {'file.ks'},
- ['kwt'] = {'file.k'},
- ['lace'] = {'file.ace', 'file.ACE'},
- ['latte'] = {'file.latte', 'file.lte'},
- ['ld'] = {'file.ld'},
- ['ldif'] = {'file.ldif'},
- ['less'] = {'file.less'},
- ['lex'] = {'file.lex', 'file.l', 'file.lxx', 'file.l++'},
- ['lftp'] = {'lftp.conf', '.lftprc', 'anylftp/rc', 'lftp/rc', 'some-lftp/rc'},
- ['lhaskell'] = {'file.lhs'},
- ['libao'] = {'/etc/libao.conf', '/.libao', 'any/.libao', 'any/etc/libao.conf'},
- ['lifelines'] = {'file.ll'},
- ['lilo'] = {'lilo.conf', 'lilo.conf-file'},
- ['limits'] = {'/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf', '/etc/limits.conf', '/etc/limits.d/file.conf', '/etc/some-limits.conf', '/etc/some-limits.d/file.conf', 'any/etc/limits', 'any/etc/limits.conf', 'any/etc/limits.d/file.conf', 'any/etc/some-limits.conf', 'any/etc/some-limits.d/file.conf'},
- ['liquid'] = {'file.liquid'},
- ['lisp'] = {'file.lsp', 'file.lisp', 'file.asd', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc'},
- ['lite'] = {'file.lite', 'file.lt'},
- ['litestep'] = {'/LiteStep/any/file.rc', 'any/LiteStep/any/file.rc'},
- ['loginaccess'] = {'/etc/login.access', 'any/etc/login.access'},
- ['logindefs'] = {'/etc/login.defs', 'any/etc/login.defs'},
- ['logtalk'] = {'file.lgt'},
- ['lotos'] = {'file.lot', 'file.lotos'},
- ['lout'] = {'file.lou', 'file.lout'},
- ['lprolog'] = {'file.sig'},
- ['lsl'] = {'file.lsl'},
- ['lss'] = {'file.lss'},
- ['lua'] = {'file.lua', 'file.rockspec', 'file.nse'},
- ['lynx'] = {'lynx.cfg'},
- ['matlab'] = {'file.m'},
- ['m3build'] = {'m3makefile', 'm3overrides'},
- ['m3quake'] = {'file.quake', 'cm3.cfg'},
- ['m4'] = {'file.at'},
- ['mail'] = {'snd.123', '.letter', '.letter.123', '.followup', '.article', '.article.123', 'pico.123', 'mutt-xx-xxx', 'muttng-xx-xxx', 'ae123.txt', 'file.eml', 'reportbug-file'},
- ['mailaliases'] = {'/etc/mail/aliases', '/etc/aliases', 'any/etc/aliases', 'any/etc/mail/aliases'},
- ['mailcap'] = {'.mailcap', 'mailcap'},
- ['make'] = {'file.mk', 'file.mak', 'file.dsp', 'makefile', 'Makefile', 'makefile-file', 'Makefile-file', 'some-makefile', 'some-Makefile'},
- ['mallard'] = {'file.page'},
- ['manconf'] = {'/etc/man.conf', 'man.config', 'any/etc/man.conf'},
- ['map'] = {'file.map'},
- ['maple'] = {'file.mv', 'file.mpl', 'file.mws'},
- ['markdown'] = {'file.markdown', 'file.mdown', 'file.mkd', 'file.mkdn', 'file.mdwn', 'file.md'},
- ['mason'] = {'file.mason', 'file.mhtml', 'file.comp'},
- ['master'] = {'file.mas', 'file.master'},
- ['mel'] = {'file.mel'},
- ['meson'] = {'meson.build', 'meson_options.txt'},
- ['messages'] = {'/log/auth', '/log/cron', '/log/daemon', '/log/debug', '/log/kern', '/log/lpr', '/log/mail', '/log/messages', '/log/news/news', '/log/syslog', '/log/user',
- '/log/auth.log', '/log/cron.log', '/log/daemon.log', '/log/debug.log', '/log/kern.log', '/log/lpr.log', '/log/mail.log', '/log/messages.log', '/log/news/news.log', '/log/syslog.log', '/log/user.log',
- '/log/auth.err', '/log/cron.err', '/log/daemon.err', '/log/debug.err', '/log/kern.err', '/log/lpr.err', '/log/mail.err', '/log/messages.err', '/log/news/news.err', '/log/syslog.err', '/log/user.err',
- '/log/auth.info', '/log/cron.info', '/log/daemon.info', '/log/debug.info', '/log/kern.info', '/log/lpr.info', '/log/mail.info', '/log/messages.info', '/log/news/news.info', '/log/syslog.info', '/log/user.info',
- '/log/auth.warn', '/log/cron.warn', '/log/daemon.warn', '/log/debug.warn', '/log/kern.warn', '/log/lpr.warn', '/log/mail.warn', '/log/messages.warn', '/log/news/news.warn', '/log/syslog.warn', '/log/user.warn',
- '/log/auth.crit', '/log/cron.crit', '/log/daemon.crit', '/log/debug.crit', '/log/kern.crit', '/log/lpr.crit', '/log/mail.crit', '/log/messages.crit', '/log/news/news.crit', '/log/syslog.crit', '/log/user.crit',
- '/log/auth.notice', '/log/cron.notice', '/log/daemon.notice', '/log/debug.notice', '/log/kern.notice', '/log/lpr.notice', '/log/mail.notice', '/log/messages.notice', '/log/news/news.notice', '/log/syslog.notice', '/log/user.notice'},
- ['mf'] = {'file.mf'},
- ['mgl'] = {'file.mgl'},
- ['mgp'] = {'file.mgp'},
- ['mib'] = {'file.mib', 'file.my'},
- ['mix'] = {'file.mix', 'file.mixal'},
- ['mma'] = {'file.nb'},
- ['mmp'] = {'file.mmp'},
- ['modconf'] = {'/etc/modules.conf', '/etc/modules', '/etc/conf.modules', '/etc/modprobe.file', 'any/etc/conf.modules', 'any/etc/modprobe.file', 'any/etc/modules', 'any/etc/modules.conf'},
- ['modula2'] = {'file.m2', 'file.mi'},
- ['monk'] = {'file.isc', 'file.monk', 'file.ssc', 'file.tsc'},
- ['moo'] = {'file.moo'},
- ['mp'] = {'file.mp'},
- ['mplayerconf'] = {'mplayer.conf', '/.mplayer/config', 'any/.mplayer/config'},
- ['mrxvtrc'] = {'mrxvtrc', '.mrxvtrc'},
- ['msidl'] = {'file.odl', 'file.mof'},
- ['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', '/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'},
- ['nanorc'] = {'/etc/nanorc', 'file.nanorc', 'any/etc/nanorc'},
- ['ncf'] = {'file.ncf'},
- ['netrc'] = {'.netrc'},
- ['nginx'] = {'file.nginx', 'nginxfile.conf', 'filenginx.conf', 'any/etc/nginx/file', 'any/usr/local/nginx/conf/file', 'any/nginx/file.conf'},
- ['ninja'] = {'file.ninja'},
- ['nqc'] = {'file.nqc'},
- ['nroff'] = {'file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom', 'tmac.file'},
- ['nsis'] = {'file.nsi', 'file.nsh'},
- ['obj'] = {'file.obj'},
- ['ocaml'] = {'file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit', 'file.mlt', 'file.mlp', 'file.mlip', 'file.mli.cppo', 'file.ml.cppo'},
- ['occam'] = {'file.occ'},
- ['octave'] = {'octaverc', '.octaverc', 'octave.conf'},
- ['omnimark'] = {'file.xom', 'file.xin'},
- ['opam'] = {'opam', 'file.opam', 'file.opam.template'},
- ['openroad'] = {'file.or'},
- ['ora'] = {'file.ora'},
- ['pamconf'] = {'/etc/pam.conf', '/etc/pam.d/file', 'any/etc/pam.conf', 'any/etc/pam.d/file'},
- ['pamenv'] = {'/etc/security/pam_env.conf', '/home/user/.pam_environment', '.pam_environment', 'pam_env.conf'},
- ['papp'] = {'file.papp', 'file.pxml', 'file.pxsl'},
- ['pascal'] = {'file.pas', 'file.dpr', 'file.lpr'},
- ['passwd'] = {'any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak', '/etc/passwd', '/etc/passwd-', '/etc/passwd.edit', '/etc/shadow', '/etc/shadow-', '/etc/shadow.edit', '/var/backups/passwd.bak', '/var/backups/shadow.bak'},
- ['pbtxt'] = {'file.pbtxt'},
- ['pccts'] = {'file.g'},
- ['pdf'] = {'file.pdf'},
- ['perl'] = {'file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'},
- ['pf'] = {'pf.conf'},
- ['pfmain'] = {'main.cf'},
- ['php'] = {'file.php', 'file.php9', 'file.phtml', 'file.ctp'},
- ['lpc'] = {'file.lpc', 'file.ulpc'},
- ['pike'] = {'file.pike', 'file.pmod'},
- ['cmod'] = {'file.cmod'},
- ['pilrc'] = {'file.rcp'},
- ['pine'] = {'.pinerc', 'pinerc', '.pinercex', 'pinercex'},
- ['pinfo'] = {'/etc/pinforc', '/.pinforc', 'any/.pinforc', 'any/etc/pinforc'},
- ['pli'] = {'file.pli', 'file.pl1'},
- ['plm'] = {'file.plm', 'file.p36', 'file.pac'},
- ['plp'] = {'file.plp'},
- ['plsql'] = {'file.pls', 'file.plsql'},
- ['po'] = {'file.po', 'file.pot'},
- ['pod'] = {'file.pod'},
- ['poke'] = {'file.pk'},
- ['postscr'] = {'file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'},
- ['pov'] = {'file.pov'},
- ['povini'] = {'.povrayrc'},
- ['ppd'] = {'file.ppd'},
- ['ppwiz'] = {'file.it', 'file.ih'},
- ['privoxy'] = {'file.action'},
- ['proc'] = {'file.pc'},
- ['procmail'] = {'.procmail', '.procmailrc'},
- ['prolog'] = {'file.pdb'},
- ['promela'] = {'file.pml'},
- ['proto'] = {'file.proto'},
- ['protocols'] = {'/etc/protocols', 'any/etc/protocols'},
- ['ps1'] = {'file.ps1', 'file.psd1', 'file.psm1', 'file.pssc'},
- ['ps1xml'] = {'file.ps1xml'},
- ['psf'] = {'file.psf'},
- ['psl'] = {'file.psl'},
- ['puppet'] = {'file.pp'},
- ['pyret'] = {'file.arr'},
- ['pyrex'] = {'file.pyx', 'file.pxd'},
- ['python'] = {'file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'},
- ['quake'] = {'anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'},
- ['radiance'] = {'file.rad', 'file.mat'},
- ['raku'] = {'file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'},
- ['ratpoison'] = {'.ratpoisonrc', 'ratpoisonrc'},
- ['rbs'] = {'file.rbs'},
- ['rc'] = {'file.rc', 'file.rch'},
- ['rcs'] = {'file,v'},
- ['readline'] = {'.inputrc', 'inputrc'},
- ['remind'] = {'.reminders', 'file.remind', 'file.rem', '.reminders-file'},
- ['rego'] = {'file.rego'},
- ['resolv'] = {'resolv.conf'},
- ['reva'] = {'file.frt'},
- ['rexx'] = {'file.rex', 'file.orx', 'file.rxo', 'file.rxj', 'file.jrexx', 'file.rexxj', 'file.rexx', 'file.testGroup', 'file.testUnit'},
- ['rib'] = {'file.rib'},
- ['rnc'] = {'file.rnc'},
- ['rng'] = {'file.rng'},
- ['robots'] = {'robots.txt'},
- ['routeros'] = {'file.rsc'},
- ['rpcgen'] = {'file.x'},
- ['rpl'] = {'file.rpl'},
- ['rst'] = {'file.rst'},
- ['rtf'] = {'file.rtf'},
- ['ruby'] = {'.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake', 'rakefile', 'Rakefile', 'rantfile', 'Rantfile', 'rakefile-file', 'Rakefile-file', 'Puppetfile'},
- ['rust'] = {'file.rs'},
- ['samba'] = {'smb.conf'},
- ['sas'] = {'file.sas'},
- ['sass'] = {'file.sass'},
- ['sather'] = {'file.sa'},
- ['sbt'] = {'file.sbt'},
- ['scala'] = {'file.scala', 'file.sc'},
- ['scheme'] = {'file.scm', 'file.ss', 'file.sld', 'file.rkt', 'file.rktd', 'file.rktl'},
- ['scilab'] = {'file.sci', 'file.sce'},
- ['screen'] = {'.screenrc', 'screenrc'},
- ['sexplib'] = {'file.sexp'},
- ['scdoc'] = {'file.scd'},
- ['scss'] = {'file.scss'},
- ['sd'] = {'file.sd'},
- ['sdc'] = {'file.sdc'},
- ['sdl'] = {'file.sdl', 'file.pr'},
- ['sed'] = {'file.sed'},
- ['sensors'] = {'/etc/sensors.conf', '/etc/sensors3.conf', 'any/etc/sensors.conf', 'any/etc/sensors3.conf'},
- ['services'] = {'/etc/services', 'any/etc/services'},
- ['setserial'] = {'/etc/serial.conf', 'any/etc/serial.conf'},
- ['sh'] = {'.bashrc', 'file.bash', '/usr/share/doc/bash-completion/filter.sh','/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf'},
- ['sieve'] = {'file.siv', 'file.sieve'},
- ['simula'] = {'file.sim'},
- ['sinda'] = {'file.sin', 'file.s85'},
- ['sisu'] = {'file.sst', 'file.ssm', 'file.ssi', 'file.-sst', 'file._sst', 'file.sst.meta', 'file.-sst.meta', 'file._sst.meta'},
- ['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'},
- ['slrnrc'] = {'.slrnrc'},
- ['slrnsc'] = {'file.score'},
- ['sm'] = {'sendmail.cf'},
- ['svelte'] = {'file.svelte'},
- ['smarty'] = {'file.tpl'},
- ['smcl'] = {'file.hlp', 'file.ihlp', 'file.smcl'},
- ['smith'] = {'file.smt', 'file.smith'},
- ['sml'] = {'file.sml'},
- ['snobol4'] = {'file.sno', 'file.spt'},
- ['sparql'] = {'file.rq', 'file.sparql'},
- ['spec'] = {'file.spec'},
- ['spice'] = {'file.sp', 'file.spice'},
- ['spup'] = {'file.speedup', 'file.spdata', 'file.spd'},
- ['spyce'] = {'file.spy', 'file.spi'},
- ['sql'] = {'file.tyb', 'file.typ', 'file.tyc', 'file.pkb', 'file.pks'},
- ['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', 'any/.ssh/file.conf'},
- ['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', 'any/etc/sudoers.d/file'},
- ['svg'] = {'file.svg'},
- ['svn'] = {'svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'},
- ['swift'] = {'file.swift'},
- ['swiftgyb'] = {'file.swift.gyb'},
- ['sil'] = {'file.sil'},
- ['sysctl'] = {'/etc/sysctl.conf', '/etc/sysctl.d/file.conf', 'any/etc/sysctl.conf', 'any/etc/sysctl.d/file.conf'},
- ['systemd'] = {'any/systemd/file.automount', 'any/systemd/file.dnssd', 'any/systemd/file.link', 'any/systemd/file.mount', 'any/systemd/file.netdev', 'any/systemd/file.network', 'any/systemd/file.nspawn', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.slice', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/some.conf.d/file.conf', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile', '/.config/systemd/user/.#', '/.config/systemd/user/.#-file', '/.config/systemd/user/file.d/.#', '/.config/systemd/user/file.d/.#-file', '/.config/systemd/user/file.d/file.conf', '/etc/systemd/file.conf.d/file.conf', '/etc/systemd/system/.#', '/etc/systemd/system/.#-file', '/etc/systemd/system/file.d/.#', '/etc/systemd/system/file.d/.#-file', '/etc/systemd/system/file.d/file.conf', '/systemd/file.automount', '/systemd/file.dnssd', '/systemd/file.link', '/systemd/file.mount', '/systemd/file.netdev', '/systemd/file.network', '/systemd/file.nspawn', '/systemd/file.path', '/systemd/file.service', '/systemd/file.slice', '/systemd/file.socket', '/systemd/file.swap', '/systemd/file.target', '/systemd/file.timer', 'any/.config/systemd/user/.#', 'any/.config/systemd/user/.#-file', 'any/.config/systemd/user/file.d/.#', 'any/.config/systemd/user/file.d/.#-file', 'any/.config/systemd/user/file.d/file.conf', 'any/etc/systemd/file.conf.d/file.conf', 'any/etc/systemd/system/.#', 'any/etc/systemd/system/.#-file', 'any/etc/systemd/system/file.d/.#', 'any/etc/systemd/system/file.d/.#-file', 'any/etc/systemd/system/file.d/file.conf'},
- ['systemverilog'] = {'file.sv', 'file.svh'},
- ['tags'] = {'tags'},
- ['tak'] = {'file.tak'},
- ['taskdata'] = {'pending.data', 'completed.data', 'undo.data'},
- ['taskedit'] = {'file.task'},
- ['tcl'] = {'file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc'},
- ['teraterm'] = {'file.ttl'},
- ['terminfo'] = {'file.ti'},
- ['tex'] = {'file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'},
- ['texinfo'] = {'file.texinfo', 'file.texi', 'file.txi'},
- ['texmf'] = {'texmf.cnf'},
- ['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'},
- ['tli'] = {'file.tli'},
- ['tmux'] = {'tmuxfile.conf', '.tmuxfile.conf', '.tmux-file.conf', '.tmux.conf', 'tmux-file.conf', 'tmux.conf', 'tmux.conf.local'},
- ['toml'] = {'file.toml', 'Gopkg.lock', 'Pipfile', '/home/user/.cargo/config'},
- ['tpp'] = {'file.tpp'},
- ['treetop'] = {'file.treetop'},
- ['trustees'] = {'trustees.conf'},
- ['tsalt'] = {'file.slt'},
- ['tsscl'] = {'file.tsscl'},
- ['tssgm'] = {'file.tssgm'},
- ['tssop'] = {'file.tssop'},
- ['twig'] = {'file.twig'},
- ['typescriptreact'] = {'file.tsx'},
- ['uc'] = {'file.uc'},
- ['udevconf'] = {'/etc/udev/udev.conf', 'any/etc/udev/udev.conf'},
- ['udevperm'] = {'/etc/udev/permissions.d/file.permissions', 'any/etc/udev/permissions.d/file.permissions'},
- ['udevrules'] = {'/etc/udev/rules.d/file.rules', '/usr/lib/udev/rules.d/file.rules', '/lib/udev/rules.d/file.rules'},
- ['uil'] = {'file.uit', 'file.uil'},
- ['updatedb'] = {'/etc/updatedb.conf', 'any/etc/updatedb.conf'},
- ['upstart'] = {'/usr/share/upstart/file.conf', '/usr/share/upstart/file.override', '/etc/init/file.conf', '/etc/init/file.override', '/.init/file.conf', '/.init/file.override', '/.config/upstart/file.conf', '/.config/upstart/file.override', 'any/.config/upstart/file.conf', 'any/.config/upstart/file.override', 'any/.init/file.conf', 'any/.init/file.override', 'any/etc/init/file.conf', 'any/etc/init/file.override', 'any/usr/share/upstart/file.conf', 'any/usr/share/upstart/file.override'},
- ['upstreamdat'] = {'upstream.dat', 'UPSTREAM.DAT', 'upstream.file.dat', 'UPSTREAM.FILE.DAT', 'file.upstream.dat', 'FILE.UPSTREAM.DAT'},
- ['upstreaminstalllog'] = {'upstreaminstall.log', 'UPSTREAMINSTALL.LOG', 'upstreaminstall.file.log', 'UPSTREAMINSTALL.FILE.LOG', 'file.upstreaminstall.log', 'FILE.UPSTREAMINSTALL.LOG'},
- ['upstreamlog'] = {'fdrupstream.log', 'upstream.log', 'UPSTREAM.LOG', 'upstream.file.log', 'UPSTREAM.FILE.LOG', 'file.upstream.log', 'FILE.UPSTREAM.LOG', 'UPSTREAM-file.log', 'UPSTREAM-FILE.LOG'},
- ['usserverlog'] = {'usserver.log', 'USSERVER.LOG', 'usserver.file.log', 'USSERVER.FILE.LOG', 'file.usserver.log', 'FILE.USSERVER.LOG'},
- ['usw2kagtlog'] = {'usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'},
- ['vb'] = {'file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'},
- ['vera'] = {'file.vr', 'file.vri', 'file.vrh'},
- ['verilog'] = {'file.v'},
- ['verilogams'] = {'file.va', 'file.vams'},
- ['vgrindefs'] = {'vgrindefs'},
- ['vhdl'] = {'file.hdl', 'file.vhd', 'file.vhdl', 'file.vbe', 'file.vst', 'file.vhdl_123', 'file.vho', 'some.vhdl_1', 'some.vhdl_1-file'},
- ['vim'] = {'file.vim', 'file.vba', '.exrc', '_exrc', 'some-vimrc', 'some-vimrc-file', 'vimrc', 'vimrc-file'},
- ['viminfo'] = {'.viminfo', '_viminfo'},
- ['vmasm'] = {'file.mar'},
- ['voscm'] = {'file.cm'},
- ['vrml'] = {'file.wrl'},
- ['vroom'] = {'file.vroom'},
- ['vue'] = {'file.vue'},
- ['wast'] = {'file.wast', 'file.wat'},
- ['webmacro'] = {'file.wm'},
- ['wget'] = {'.wgetrc', 'wgetrc'},
- ['winbatch'] = {'file.wbt'},
- ['wml'] = {'file.wml'},
- ['wsh'] = {'file.wsf', 'file.wsc'},
- ['wsml'] = {'file.wsml'},
- ['wvdial'] = {'wvdial.conf', '.wvdialrc'},
- ['xdefaults'] = {'.Xdefaults', '.Xpdefaults', '.Xresources', 'xdm-config', 'file.ad', '/Xresources/file', '/app-defaults/file', 'Xresources', 'Xresources-file', 'any/Xresources/file', 'any/app-defaults/file'},
- ['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.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'},
- ['xpm2'] = {'file.xpm2'},
- ['xquery'] = {'file.xq', 'file.xql', 'file.xqm', 'file.xquery', 'file.xqy'},
- ['xs'] = {'file.xs'},
- ['xsd'] = {'file.xsd'},
- ['xslt'] = {'file.xsl', 'file.xslt'},
- ['yacc'] = {'file.yy', 'file.yxx', 'file.y++'},
- ['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'},
- ['help'] = {funcs.getenv('VIMRUNTIME') .. '/doc/help.txt'},
- }
-
- local filename_case_checks = {
- ['modula2'] = {'file.DEF', 'file.MOD'},
- ['bzl'] = {'file.BUILD', 'BUILD'},
- }
-
- local function check_items(checks)
- meths.set_option('swapfile', false)
- for ft, names in pairs(checks) do
- for _, name in ipairs(names) do
- meths.command('new')
- meths.command('edit ' .. funcs.fnameescape(name))
- if curbufmeths.get_option('filetype') == '' and curbufmeths.get_option('readonly') then -- luacheck: ignore 542
- -- File exists but not able to edit it (permission denied)
- else
- eq(ft, curbufmeths.get_option('filetype'))
- end
- meths.command('bwipe!')
- end
- end
- meths.command('set swapfile&')
- end
-
- meths.set_var('do_filetype_lua', 1)
- meths.command('filetype on')
- check_items(filename_checks)
- if funcs.has('fname_case') == 1 then
- check_items(filename_case_checks)
- end
- meths.command('filetype off')
- end)
end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 642dc59bbc..ddcf7687f5 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -987,6 +987,38 @@ describe('lua stdlib', function()
matches([[attempt to index .* nil value]],
pcall_err(exec_lua, 'return vim.g[0].testing'))
+
+ exec_lua [[
+ local counter = 0
+ vim.g.AddCounter = function() counter = counter + 1 end
+ vim.g.GetCounter = function() return counter end
+ ]]
+
+ eq(0, eval('g:GetCounter()'))
+ eval('g:AddCounter()')
+ eq(1, eval('g:GetCounter()'))
+ eval('g:AddCounter()')
+ eq(2, eval('g:GetCounter()'))
+ exec_lua([[vim.g.AddCounter()]])
+ eq(3, exec_lua([[return vim.g.GetCounter()]]))
+ exec_lua([[vim.api.nvim_get_var('AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_get_var('GetCounter')()]]))
+
+ exec_lua [[
+ local counter = 0
+ vim.api.nvim_set_var('AddCounter', function() counter = counter + 1 end)
+ vim.api.nvim_set_var('GetCounter', function() return counter end)
+ ]]
+
+ eq(0, eval('g:GetCounter()'))
+ eval('g:AddCounter()')
+ eq(1, eval('g:GetCounter()'))
+ eval('g:AddCounter()')
+ eq(2, eval('g:GetCounter()'))
+ exec_lua([[vim.g.AddCounter()]])
+ eq(3, exec_lua([[return vim.g.GetCounter()]]))
+ exec_lua([[vim.api.nvim_get_var('AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_get_var('GetCounter')()]]))
end)
it('vim.b', function()
@@ -1022,6 +1054,38 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.b.to_delete")
exec_lua [[
+ local counter = 0
+ vim.b.AddCounter = function() counter = counter + 1 end
+ vim.b.GetCounter = function() return counter end
+ ]]
+
+ eq(0, eval('b:GetCounter()'))
+ eval('b:AddCounter()')
+ eq(1, eval('b:GetCounter()'))
+ eval('b:AddCounter()')
+ eq(2, eval('b:GetCounter()'))
+ exec_lua([[vim.b.AddCounter()]])
+ eq(3, exec_lua([[return vim.b.GetCounter()]]))
+ exec_lua([[vim.api.nvim_buf_get_var(0, 'AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_buf_get_var(0, 'GetCounter')()]]))
+
+ exec_lua [[
+ local counter = 0
+ vim.api.nvim_buf_set_var(0, 'AddCounter', function() counter = counter + 1 end)
+ vim.api.nvim_buf_set_var(0, 'GetCounter', function() return counter end)
+ ]]
+
+ eq(0, eval('b:GetCounter()'))
+ eval('b:AddCounter()')
+ eq(1, eval('b:GetCounter()'))
+ eval('b:AddCounter()')
+ eq(2, eval('b:GetCounter()'))
+ exec_lua([[vim.b.AddCounter()]])
+ eq(3, exec_lua([[return vim.b.GetCounter()]]))
+ exec_lua([[vim.api.nvim_buf_get_var(0, 'AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_buf_get_var(0, 'GetCounter')()]]))
+
+ exec_lua [[
vim.cmd "vnew"
]]
@@ -1059,6 +1123,38 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.w.to_delete")
exec_lua [[
+ local counter = 0
+ vim.w.AddCounter = function() counter = counter + 1 end
+ vim.w.GetCounter = function() return counter end
+ ]]
+
+ eq(0, eval('w:GetCounter()'))
+ eval('w:AddCounter()')
+ eq(1, eval('w:GetCounter()'))
+ eval('w:AddCounter()')
+ eq(2, eval('w:GetCounter()'))
+ exec_lua([[vim.w.AddCounter()]])
+ eq(3, exec_lua([[return vim.w.GetCounter()]]))
+ exec_lua([[vim.api.nvim_win_get_var(0, 'AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_win_get_var(0, 'GetCounter')()]]))
+
+ exec_lua [[
+ local counter = 0
+ vim.api.nvim_win_set_var(0, 'AddCounter', function() counter = counter + 1 end)
+ vim.api.nvim_win_set_var(0, 'GetCounter', function() return counter end)
+ ]]
+
+ eq(0, eval('w:GetCounter()'))
+ eval('w:AddCounter()')
+ eq(1, eval('w:GetCounter()'))
+ eval('w:AddCounter()')
+ eq(2, eval('w:GetCounter()'))
+ exec_lua([[vim.w.AddCounter()]])
+ eq(3, exec_lua([[return vim.w.GetCounter()]]))
+ exec_lua([[vim.api.nvim_win_get_var(0, 'AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_win_get_var(0, 'GetCounter')()]]))
+
+ exec_lua [[
vim.cmd "vnew"
]]
@@ -1091,6 +1187,38 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.t.to_delete")
exec_lua [[
+ local counter = 0
+ vim.t.AddCounter = function() counter = counter + 1 end
+ vim.t.GetCounter = function() return counter end
+ ]]
+
+ eq(0, eval('t:GetCounter()'))
+ eval('t:AddCounter()')
+ eq(1, eval('t:GetCounter()'))
+ eval('t:AddCounter()')
+ eq(2, eval('t:GetCounter()'))
+ exec_lua([[vim.t.AddCounter()]])
+ eq(3, exec_lua([[return vim.t.GetCounter()]]))
+ exec_lua([[vim.api.nvim_tabpage_get_var(0, 'AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'GetCounter')()]]))
+
+ exec_lua [[
+ local counter = 0
+ vim.api.nvim_tabpage_set_var(0, 'AddCounter', function() counter = counter + 1 end)
+ vim.api.nvim_tabpage_set_var(0, 'GetCounter', function() return counter end)
+ ]]
+
+ eq(0, eval('t:GetCounter()'))
+ eval('t:AddCounter()')
+ eq(1, eval('t:GetCounter()'))
+ eval('t:AddCounter()')
+ eq(2, eval('t:GetCounter()'))
+ exec_lua([[vim.t.AddCounter()]])
+ eq(3, exec_lua([[return vim.t.GetCounter()]]))
+ exec_lua([[vim.api.nvim_tabpage_get_var(0, 'AddCounter')()]])
+ eq(4, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'GetCounter')()]]))
+
+ exec_lua [[
vim.cmd "tabnew"
]]
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 2fe7eca260..eab520948f 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -76,7 +76,7 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options)
end;
flags = {
allow_incremental_sync = options.allow_incremental_sync or false;
- debounce_text_changes = 0;
+ debounce_text_changes = options.debounce_text_changes or 0;
};
on_exit = function(...)
vim.rpcnotify(1, "exit", ...)
@@ -929,7 +929,60 @@ describe('LSP', function()
local client
test_rpc_server {
test_name = "basic_check_buffer_open_and_change_incremental";
- options = { allow_incremental_sync = true };
+ options = {
+ allow_incremental_sync = true,
+ };
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental")
+ eq(sync_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end;
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
+ exec_lua [[
+ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
+ "123boop";
+ })
+ ]]
+ client.notify('finish')
+ end
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+ it('should check the body and didChange incremental with debounce', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open_and_change_incremental";
+ options = {
+ allow_incremental_sync = true,
+ debounce_text_changes = 5
+ };
on_setup = function()
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
@@ -1238,7 +1291,7 @@ describe('LSP', function()
make_edit(2, 0, 2, 2, {"3"});
make_edit(3, 2, 3, 4, {""});
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'123First line of text';
'2econd line of text';
@@ -1258,7 +1311,7 @@ describe('LSP', function()
make_edit(3, #'', 3, #"Fourth", {"another line of text", "before this"});
make_edit(3, #'Fourth', 3, #"Fourth line of text", {"!"});
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'';
'123';
@@ -1282,7 +1335,7 @@ describe('LSP', function()
make_edit(3, #"Fourth", 3, #'', {"another line of text", "before this"});
make_edit(3, #"Fourth line of text", 3, #'Fourth', {"!"});
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'';
'123';
@@ -1299,7 +1352,7 @@ describe('LSP', function()
local edits = {
make_edit(4, 3, 4, 4, {"ä"});
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
'Second line of text';
@@ -1312,7 +1365,7 @@ describe('LSP', function()
local edits = {
make_edit(5, 0, 5, 0, "foobar");
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
'Second line of text';
@@ -1322,6 +1375,20 @@ describe('LSP', function()
'foobar';
}, buf_lines(1))
end)
+ it('applies multiple text edits at the end of the document', function()
+ local edits = {
+ make_edit(4, 0, 5, 0, "");
+ make_edit(5, 0, 5, 0, "foobar");
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
+ eq({
+ 'First line of text';
+ 'Second line of text';
+ 'Third line of text';
+ 'Fourth line of text';
+ 'foobar';
+ }, buf_lines(1))
+ end)
describe('cursor position', function()
it('don\'t fix the cursor if the range contains the cursor', function()
@@ -1329,7 +1396,7 @@ describe('LSP', function()
local edits = {
make_edit(1, 0, 1, 19, 'Second line of text')
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
'Second line of text';
@@ -1346,7 +1413,7 @@ describe('LSP', function()
make_edit(1, 0, 1, 6, ''),
make_edit(1, 6, 1, 19, '')
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
'';
@@ -1363,7 +1430,7 @@ describe('LSP', function()
make_edit(1, 0, 1, 6, ''),
make_edit(0, 18, 5, 0, '')
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
}, buf_lines(1))
@@ -1375,7 +1442,7 @@ describe('LSP', function()
local edits = {
make_edit(1, 0, 2, 0, '')
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
'Third line of text';
@@ -1390,7 +1457,7 @@ describe('LSP', function()
local edits = {
make_edit(1, 7, 1, 11, '')
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
'Second of text';
@@ -1406,7 +1473,7 @@ describe('LSP', function()
local edits = {
make_edit(0, 11, 1, 12, '')
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({
'First line of text';
'Third line of text';
@@ -1422,21 +1489,21 @@ describe('LSP', function()
local edits = {
make_edit(0, 0, 5, 0, {"All replaced"});
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({'All replaced'}, buf_lines(1))
end)
it('applies edits when the end line is 2 larger than vim\'s', function()
local edits = {
make_edit(0, 0, 6, 0, {"All replaced"});
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({'All replaced'}, buf_lines(1))
end)
it('applies edits with a column offset', function()
local edits = {
make_edit(0, 0, 5, 2, {"All replaced"});
}
- exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
eq({'All replaced'}, buf_lines(1))
end)
end)
@@ -1464,7 +1531,7 @@ describe('LSP', function()
]]
end)
it('correctly goes ahead with the edit if all is normal', function()
- exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(5))
+ exec_lua("vim.lsp.util.apply_text_document_edit(..., nil, 'utf-16')", text_document_edit(5))
eq({
'First ↥ 🤦 🦄 line of text';
'2nd line of 语text';
@@ -1476,7 +1543,7 @@ describe('LSP', function()
local bufnr = select(1, ...)
local text_edit = select(2, ...)
vim.lsp.util.buf_versions[bufnr] = 10
- vim.lsp.util.apply_text_document_edit(text_edit)
+ vim.lsp.util.apply_text_document_edit(text_edit, nil, 'utf-16')
]], target_bufnr, text_document_edit(0))
eq({
'First ↥ 🤦 🦄 line of text';
@@ -1489,7 +1556,7 @@ describe('LSP', function()
local args = {...}
local versionedBuf = args[2]
vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion
- vim.lsp.util.apply_text_document_edit(args[1])
+ vim.lsp.util.apply_text_document_edit(args[1], nil, 'utf-16')
]], edit, versionedBuf)
end
@@ -1515,17 +1582,36 @@ describe('LSP', function()
describe('workspace_apply_edit', function()
it('workspace/applyEdit returns ApplyWorkspaceEditResponse', function()
- local expected = {
- applied = true;
- failureReason = nil;
+ local expected_handlers = {
+ {NIL, {}, {method="test", client_id=1}};
+ }
+ test_rpc_server {
+ test_name = "basic_init";
+ on_init = function(client, _)
+ client.stop()
+ end;
+ -- If the program timed out, then code will be nil.
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end;
+ -- Note that NIL must be used here.
+ -- on_handler(err, method, result, client_id)
+ on_handler = function(...)
+ local expected = {
+ applied = true;
+ failureReason = nil;
+ }
+ eq(expected, exec_lua [[
+ local apply_edit = {
+ label = nil;
+ edit = {};
+ }
+ return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit, {client_id = TEST_RPC_CLIENT_ID})
+ ]])
+ eq(table.remove(expected_handlers), {...})
+ end;
}
- eq(expected, exec_lua [[
- local apply_edit = {
- label = nil;
- edit = {};
- }
- return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit)
- ]])
end)
end)
@@ -1599,7 +1685,7 @@ describe('LSP', function()
local workspace_edits = args[1]
local target_bufnr = args[2]
- vim.lsp.util.apply_workspace_edit(workspace_edits)
+ vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16')
return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
]], make_workspace_edit(edits), target_bufnr))
@@ -1621,7 +1707,7 @@ describe('LSP', function()
local workspace_edits = args[1]
local target_bufnr = args[2]
- vim.lsp.util.apply_workspace_edit(workspace_edits)
+ vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16')
return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
]], make_workspace_edit(edits), target_bufnr))
@@ -1638,7 +1724,7 @@ describe('LSP', function()
},
}
}
- exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
+ exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')
eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
end)
it('createFile does not touch file if it exists and ignoreIfExists is set', function()
@@ -1656,7 +1742,7 @@ describe('LSP', function()
},
}
}
- exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
+ exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')
eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
eq('Dummy content', read_file(tmpfile))
end)
@@ -1676,7 +1762,7 @@ describe('LSP', function()
},
}
}
- exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
+ exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')
eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
eq('', read_file(tmpfile))
end)
@@ -1697,7 +1783,7 @@ describe('LSP', function()
}
}
}
- eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit))
+ eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16'))
eq(false, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
eq(false, exec_lua('return vim.api.nvim_buf_is_loaded(vim.fn.bufadd(...))', tmpfile))
end)
@@ -1858,7 +1944,7 @@ describe('LSP', function()
}
},
}
- return vim.lsp.util.locations_to_items(locations)
+ return vim.lsp.util.locations_to_items(locations, 'utf-16')
]]
eq(expected, actual)
end)
@@ -1888,7 +1974,7 @@ describe('LSP', function()
}
},
}
- return vim.lsp.util.locations_to_items(locations)
+ return vim.lsp.util.locations_to_items(locations, 'utf-16')
]]
eq(expected, actual)
end)
@@ -2192,7 +2278,7 @@ describe('LSP', function()
end
local jump = function(msg)
- eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg))
+ eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg, "utf-16"))
eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]])
return {
line = exec_lua[[return vim.fn.line('.')]],
@@ -2266,6 +2352,27 @@ describe('LSP', function()
end)
end)
+ describe('lsp.util.convert_signature_help_to_markdown_lines', function()
+ it('can handle negative activeSignature', function()
+ local result = exec_lua[[
+ local signature_help = {
+ activeParameter = 0,
+ activeSignature = -1,
+ signatures = {
+ {
+ documentation = "",
+ label = "TestEntity.TestEntity()",
+ parameters = {}
+ },
+ }
+ }
+ return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'cs', {','})
+ ]]
+ local expected = {'```cs', 'TestEntity.TestEntity()', '```', ''}
+ eq(expected, result)
+ end)
+ end)
+
describe('lsp.util.get_effective_tabstop', function()
local function test_tabstop(tabsize, softtabstop)
exec_lua(string.format([[
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 1ff451434d..beb43e0271 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -292,10 +292,9 @@ describe(':terminal buffer', function()
command('quit')
end)
- it('does not segfault when pasting empty buffer #13955', function()
- feed_command('terminal')
+ it('does not segfault when pasting empty register #13955', function()
feed('<c-\\><c-n>')
- feed_command('put a') -- buffer a is empty
+ feed_command('put a') -- register a is empty
helpers.assert_alive()
end)
end)
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index 780a0b9b5a..6e2c851df7 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers')
local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval
-local feed, nvim = helpers.feed, helpers.nvim
+local feed, nvim, command = helpers.feed, helpers.nvim, helpers.command
local feed_data = thelpers.feed_data
describe(':terminal mouse', function()
@@ -10,9 +10,9 @@ describe(':terminal mouse', function()
before_each(function()
clear()
nvim('set_option', 'statusline', '==========')
- nvim('command', 'highlight StatusLine cterm=NONE')
- nvim('command', 'highlight StatusLineNC cterm=NONE')
- nvim('command', 'highlight VertSplit cterm=NONE')
+ command('highlight StatusLine cterm=NONE')
+ command('highlight StatusLineNC cterm=NONE')
+ command('highlight VertSplit cterm=NONE')
screen = thelpers.screen_setup()
local lines = {}
for i = 1, 30 do
@@ -38,6 +38,26 @@ describe(':terminal mouse', function()
eq('nt', eval('mode(1)'))
end)
+ it('will exit focus and trigger Normal mode mapping on mouse click', function()
+ command('let g:got_leftmouse = 0')
+ command('nnoremap <LeftMouse> <Cmd>let g:got_leftmouse = 1<CR>')
+ eq('t', eval('mode(1)'))
+ eq(0, eval('g:got_leftmouse'))
+ feed('<LeftMouse>')
+ eq('nt', eval('mode(1)'))
+ eq(1, eval('g:got_leftmouse'))
+ end)
+
+ it('will exit focus and trigger Normal mode mapping on mouse click with modifier', function()
+ command('let g:got_ctrl_leftmouse = 0')
+ command('nnoremap <C-LeftMouse> <Cmd>let g:got_ctrl_leftmouse = 1<CR>')
+ eq('t', eval('mode(1)'))
+ eq(0, eval('g:got_ctrl_leftmouse'))
+ feed('<C-LeftMouse>')
+ eq('nt', eval('mode(1)'))
+ eq(1, eval('g:got_ctrl_leftmouse'))
+ end)
+
it('will exit focus on <C-\\> + mouse-scroll', function()
eq('t', eval('mode(1)'))
feed('<C-\\>')
@@ -180,7 +200,7 @@ describe(':terminal mouse', function()
it('will forward mouse clicks to the program with the correct even if set nu', function()
if helpers.pending_win32(pending) then return end
- nvim('command', 'set number')
+ command('set number')
-- When the display area such as a number is clicked, it returns to the
-- normal mode.
feed('<LeftMouse><3,0>')
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 911fa017ab..396fe5feab 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -167,6 +167,27 @@ void ui_refresh(void)
eq('variable', ret)
end)
+ it("supports caching queries", function()
+ local long_query = query:rep(100)
+ local first_run = exec_lua ([[
+ local before = vim.loop.hrtime()
+ cquery = vim.treesitter.parse_query("c", ...)
+ local after = vim.loop.hrtime()
+ return after - before
+ ]], long_query)
+
+ local subsequent_runs = exec_lua ([[
+ local before = vim.loop.hrtime()
+ for i=1,100,1 do
+ cquery = vim.treesitter.parse_query("c", ...)
+ end
+ local after = vim.loop.hrtime()
+ return after - before
+ ]], long_query)
+
+ assert.True(1000 * subsequent_runs < first_run)
+ end)
+
it('support query and iter by capture', function()
insert(test_text)
@@ -645,6 +666,21 @@ int x = INT_MAX;
-- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y))
}, get_ranges())
end)
+
+ it("should not inject bad languages", function()
+ if helpers.pending_win32(pending) then return end
+ exec_lua([=[
+ vim.treesitter.add_directive("inject-bad!", function(match, _, _, pred, metadata)
+ metadata.language = "{"
+ metadata.combined = true
+ metadata.content = pred[2]
+ end)
+
+ parser = vim.treesitter.get_parser(0, "c", {
+ injections = {
+ c = "(preproc_function_def value: ((preproc_arg) @_a (#inject-bad! @_a)))"}})
+ ]=])
+ end)
end)
describe("when using the offset directive", function()
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
index 16ed3b9486..9f84b71ece 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -755,12 +755,26 @@ describe('Buffer highlighting', function()
-- TODO: only a virtual text from the same ns curretly overrides
-- an existing virtual text. We might add a prioritation system.
set_virtual_text(id1, 0, s1, {})
- eq({{1, 0, 0, { priority = 0, virt_text = s1}}}, get_extmarks(id1, {0,0}, {0, -1}, {details=true}))
+ eq({{1, 0, 0, {
+ priority = 0,
+ virt_text = s1,
+ -- other details
+ right_gravity = true,
+ virt_text_pos = 'eol',
+ virt_text_hide = false,
+ }}}, get_extmarks(id1, {0,0}, {0, -1}, {details=true}))
-- TODO: is this really valid? shouldn't the max be line_count()-1?
local lastline = line_count()
set_virtual_text(id1, line_count(), s2, {})
- eq({{3, lastline, 0, { priority = 0, virt_text = s2}}}, get_extmarks(id1, {lastline,0}, {lastline, -1}, {details=true}))
+ eq({{3, lastline, 0, {
+ priority = 0,
+ virt_text = s2,
+ -- other details
+ right_gravity = true,
+ virt_text_pos = 'eol',
+ virt_text_hide = false,
+ }}}, get_extmarks(id1, {lastline,0}, {lastline, -1}, {details=true}))
eq({}, get_extmarks(id1, {lastline+9000,0}, {lastline+9000, -1}, {}))
end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index ea8968a653..11718a6e18 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -114,11 +114,30 @@ describe('mappings', function()
end)
end)
-describe('input utf sequences that contain CSI/K_SPECIAL', function()
+describe('input utf sequences that contain K_SPECIAL (0x80)', function()
it('ok', function()
feed('i…<esc>')
expect('…')
end)
+
+ it('can be mapped', function()
+ command('inoremap … E280A6')
+ feed('i…<esc>')
+ expect('E280A6')
+ end)
+end)
+
+describe('input utf sequences that contain CSI (0x9B)', function()
+ it('ok', function()
+ feed('iě<esc>')
+ expect('ě')
+ end)
+
+ it('can be mapped', function()
+ command('inoremap ě C49B')
+ feed('iě<esc>')
+ expect('C49B')
+ end)
end)
describe('input non-printable chars', function()
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index 4fc5c389e5..c44e59cfd3 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -10,6 +10,7 @@ local funcs = helpers.funcs
local get_pathsep = helpers.get_pathsep
local eq = helpers.eq
local pcall_err = helpers.pcall_err
+local exec_lua = helpers.exec_lua
describe('ui/ext_popupmenu', function()
local screen
@@ -25,6 +26,7 @@ describe('ui/ext_popupmenu', function()
[5] = {bold = true, foreground = Screen.colors.SeaGreen},
[6] = {background = Screen.colors.WebGray},
[7] = {background = Screen.colors.LightMagenta},
+ [8] = {foreground = Screen.colors.Red},
})
source([[
function! TestComplete() abort
@@ -369,6 +371,111 @@ describe('ui/ext_popupmenu', function()
{1:~ }|
{2:-- INSERT --} |
]])
+
+ command('iunmap <f1>')
+ command('iunmap <f2>')
+ command('iunmap <f3>')
+ exec_lua([[
+ vim.keymap.set('i', '<f1>', function() vim.api.nvim_select_popupmenu_item(2, true, false, {}) end)
+ vim.keymap.set('i', '<f2>', function() vim.api.nvim_select_popupmenu_item(-1, false, false, {}) end)
+ vim.keymap.set('i', '<f3>', function() vim.api.nvim_select_popupmenu_item(1, false, true, {}) end)
+ ]])
+ feed('<C-r>=TestComplete()<CR>')
+ screen:expect([[
+ |
+ foo^ |
+ {6:fo x the foo }{1: }|
+ {7:bar }{1: }|
+ {7:spam }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<f1>')
+ screen:expect([[
+ |
+ spam^ |
+ {7:fo x the foo }{1: }|
+ {7:bar }{1: }|
+ {6:spam }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<f2>')
+ screen:expect([[
+ |
+ spam^ |
+ {7:fo x the foo }{1: }|
+ {7:bar }{1: }|
+ {7:spam }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<f3>')
+ screen:expect([[
+ |
+ bar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<esc>ddiaa bb cc<cr>')
+ feed('<c-x><c-n>')
+ screen:expect([[
+ aa bb cc |
+ aa^ |
+ {6:aa }{1: }|
+ {7:bb }{1: }|
+ {7:cc }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 3} |
+ ]])
+
+ feed('<f1>')
+ screen:expect([[
+ aa bb cc |
+ cc^ |
+ {7:aa }{1: }|
+ {7:bb }{1: }|
+ {6:cc }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 3 of 3} |
+ ]])
+
+ feed('<f2>')
+ screen:expect([[
+ aa bb cc |
+ cc^ |
+ {7:aa }{1: }|
+ {7:bb }{1: }|
+ {7:cc }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- Keyword Local completion (^N^P) }{8:Back at original} |
+ ]])
+
+ feed('<f3>')
+ screen:expect([[
+ aa bb cc |
+ bb^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
end)
local function source_complete_month()
@@ -2214,6 +2321,47 @@ describe('builtin popupmenu', function()
assert_alive()
end)
+ it('is closed by :stopinsert from timer #12976', function()
+ screen:try_resize(32,14)
+ command([[call setline(1, ['hello', 'hullo', 'heeee', ''])]])
+ feed('Gah<C-N>')
+ screen:expect([[
+ hello |
+ hullo |
+ heeee |
+ hello^ |
+ {s:hello }{1: }|
+ {n:hullo }{1: }|
+ {n:heeee }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ command([[call timer_start(100, { -> execute('stopinsert') })]])
+ helpers.sleep(200)
+ feed('k') -- cursor should move up in Normal mode
+ screen:expect([[
+ hello |
+ hullo |
+ heee^e |
+ hello |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
it('truncates double-width character correctly when there is no scrollbar', function()
screen:try_resize(32,8)
command('set completeopt+=menuone,noselect')
diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua
index 10d02d2eb4..3c96bc5f58 100644
--- a/test/unit/marktree_spec.lua
+++ b/test/unit/marktree_spec.lua
@@ -32,11 +32,11 @@ local function shadoworder(tree, shadow, iter, giveorder)
local mark = lib.marktree_itr_current(iter)
local id = tonumber(mark.id)
local spos = shadow[id]
- if (mark.row ~= spos[1] or mark.col ~= spos[2]) then
- error("invalid pos for "..id..":("..mark.row..", "..mark.col..") instead of ("..spos[1]..", "..spos[2]..")")
+ if (mark.pos.row ~= spos[1] or mark.pos.col ~= spos[2]) then
+ error("invalid pos for "..id..":("..mark.pos.row..", "..mark.pos.col..") instead of ("..spos[1]..", "..spos[2]..")")
end
- if mark.right_gravity ~= spos[3] then
- error("invalid gravity for "..id..":("..mark.row..", "..mark.col..")")
+ if lib.mt_right_test(mark) ~= spos[3] then
+ error("invalid gravity for "..id..":("..mark.pos.row..", "..mark.pos.col..")")
end
if count > 0 then
if not pos_leq(last, spos) then
@@ -87,7 +87,21 @@ local function dosplice(tree, shadow, start, old_extent, new_extent)
shadowsplice(shadow, start, old_extent, new_extent)
end
+local last_id = nil
+
+local function put(tree, row, col, gravitate)
+ last_id = last_id + 1
+ local my_id = last_id
+
+ lib.marktree_put_test(tree, my_id, row, col, gravitate);
+ return my_id
+end
+
describe('marktree', function()
+ before_each(function()
+ last_id = 0
+ end)
+
itp('works', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
local shadow = {}
@@ -97,7 +111,7 @@ describe('marktree', function()
for i = 1,100 do
for j = 1,100 do
local gravitate = (i%2) > 0
- local id = tonumber(lib.marktree_put(tree, j, i, gravitate, 0))
+ local id = put(tree, j, i, gravitate)
ok(id > 0)
eq(nil, shadow[id])
shadow[id] = {j,i,gravitate}
@@ -115,12 +129,12 @@ describe('marktree', function()
eq({}, id2pos)
for i,ipos in pairs(shadow) do
- local pos = lib.marktree_lookup(tree, i, iter)
- eq(ipos[1], pos.row)
- eq(ipos[2], pos.col)
+ local p = lib.marktree_lookup_ns(tree, -1, i, false, iter)
+ eq(ipos[1], p.pos.row)
+ eq(ipos[2], p.pos.col)
local k = lib.marktree_itr_current(iter)
- eq(ipos[1], k.row)
- eq(ipos[2], k.col, ipos[1])
+ eq(ipos[1], k.pos.row)
+ eq(ipos[2], k.pos.col, ipos[1])
lib.marktree_itr_next(tree, iter)
-- TODO(bfredl): use id2pos to check neighbour?
-- local k2 = lib.marktree_itr_current(iter)
@@ -130,8 +144,8 @@ describe('marktree', function()
lib.marktree_itr_get(tree, ipos[1], ipos[2], iter)
local k = lib.marktree_itr_current(iter)
eq(i, tonumber(k.id))
- eq(ipos[1], k.row)
- eq(ipos[2], k.col)
+ eq(ipos[1], k.pos.row)
+ eq(ipos[2], k.pos.col)
end
ok(lib.marktree_itr_first(tree, iter))
@@ -146,8 +160,8 @@ describe('marktree', function()
lib.marktree_itr_get(tree, i, 50+ci, iter)
local k = lib.marktree_itr_current(iter)
local id = tonumber(k.id)
- eq(shadow[id][1], k.row)
- eq(shadow[id][2], k.col)
+ eq(shadow[id][1], k.pos.row)
+ eq(shadow[id][2], k.pos.col)
lib.marktree_del_itr(tree, iter, false)
shadow[id] = nil
end
@@ -191,7 +205,7 @@ describe('marktree', function()
-- https://github.com/neovim/neovim/pull/14719
lib.marktree_clear(tree)
for i = 1,20 do
- lib.marktree_put(tree, i, i, false, 0)
+ put(tree, i, i, false)
end
lib.marktree_itr_get(tree, 10, 10, iter)
diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua
index 15ce59747e..fb476397e6 100644
--- a/test/unit/path_spec.lua
+++ b/test/unit/path_spec.lua
@@ -626,4 +626,20 @@ describe('path.c', function()
eq(false, path_with_extension('/some/path/file', 'lua'))
end)
end)
+
+ describe('path_with_url', function()
+ itp('scheme is alpha and inner hyphen only', function()
+ local function path_with_url(fname)
+ return cimp.path_with_url(to_cstr(fname))
+ end
+ eq(1, path_with_url([[test://xyz/foo/b0]]))
+ eq(2, path_with_url([[test:\\xyz\foo\b0]]))
+ eq(0, path_with_url([[test+abc://xyz/foo/b1]]))
+ eq(0, path_with_url([[test_abc://xyz/foo/b2]]))
+ eq(1, path_with_url([[test-abc://xyz/foo/b3]]))
+ eq(2, path_with_url([[test-abc:\\xyz\foo\b3]]))
+ eq(0, path_with_url([[-test://xyz/foo/b4]]))
+ eq(0, path_with_url([[test-://xyz/foo/b5]]))
+ end)
+ end)
end)
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index 613b39c833..cdca7c7b9c 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -203,8 +203,8 @@ set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc891
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/v0.20.2.tar.gz)
-set(TREESITTER_SHA256 2a0445f8172bbf83db005aedb4e893d394e2b7b33251badd3c94c2c5cc37c403)
+set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.3.tar.gz)
+set(TREESITTER_SHA256 ab52fe93e0c658cff656b9d10d67cdd29084247052964eba13ed6f0e9fa3bd36)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)